import sys
import os
import shutil
import pandas as pd
import numpy as np
import cv2
from datetime import datetime
import chardet
import re  # 添加 re 模块的导入
import json
import folium
import asyncio
from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout,
                             QHBoxLayout, QPushButton, QLabel, QStackedWidget,
                             QFileDialog, QProgressBar, QFrame, QTextEdit, QLineEdit, QMessageBox, QGroupBox, QComboBox,
                             QListWidget, QDialog, QTableWidget, QTableWidgetItem, QHeaderView,
                             QGridLayout, QCheckBox, QScrollArea, QSpinBox, QDoubleSpinBox,
                             QTabWidget, QDialogButtonBox, QApplication, QGraphicsView, QGraphicsScene,
                             QGraphicsEllipseItem)
from PyQt5.QtCore import Qt, QTimer, QPropertyAnimation, QEasingCurve, QSize, pyqtSignal, QThread, QDateTime
from PyQt5.QtGui import QFont, QIcon, QPainter, QColor, QPen, QImage, QPixmap
from PyQt5.QtWebEngineWidgets import QWebEngineView
import pyqtgraph as pg
from traffic_analysis.analysis.traffic_analyzer import TrafficAnalyzer
from traffic_analysis.data_processing.data_processor import DataProcessor
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from traffic_analysis.models.model_comparison import ModelComparison
from traffic_analysis.models.ml_nn_modules import MLModels, NeuralNetworks
import math
import seaborn as sns


class GlowButton(QPushButton):
    def __init__(self, text, parent=None):
        super().__init__(text, parent)
        self.setFixedSize(220, 60)
        self.setFont(QFont("Microsoft YaHei", 11, QFont.Bold))
        self.setup_animations()

    def setup_animations(self):
        self._animation = QPropertyAnimation(self, b"size")
        self._animation.setDuration(200)
        self._animation.setEasingCurve(QEasingCurve.OutQuad)

        self.setStyleSheet("""
            QPushButton {
                background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1,
                                                stop:0 #1a237e, stop:1 #0d47a1);
                color: white;
                border-radius: 15px;
                border: 2px solid #4fc3f7;
            }
            QPushButton:hover {
                background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1,
                                                stop:0 #0d47a1, stop:1 #1a237e);
                border: 2px solid #03a9f4;
            }
            QPushButton:pressed {
                background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1,
                                                stop:0 #01579b, stop:1 #0d47a1);
            }
        """)

    def enterEvent(self, event):
        self._animation.setStartValue(self.size())
        self._animation.setEndValue(QSize(230, 65))
        self._animation.start()
        super().enterEvent(event)

    def leaveEvent(self, event):
        self._animation.setStartValue(self.size())
        self._animation.setEndValue(QSize(220, 60))
        self._animation.start()
        super().leaveEvent(event)


class DashboardWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setup_ui()
        # 创建定时器更新时间
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_datetime)
        self.timer.start(1000)  # 每秒更新一次

        # 添加定时器动态更新预测数据
        self.prediction_timer = QTimer(self)
        self.prediction_timer.timeout.connect(self.update_prediction_data)
        self.prediction_timer.start(5000)  # 每5秒更新一次
        self.update_prediction_data()

    def setup_ui(self):
        # 创建主滚动区域
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        scroll.setStyleSheet("""
                    QScrollArea {
                        border: none;
                        background-color: transparent;
                    }
                    QScrollBar:vertical {
                        border: none;
                        background: rgba(13, 71, 161, 0.1);
                        width: 10px;
                        border-radius: 5px;
                    }
                    QScrollBar::handle:vertical {
                        background: rgba(79, 195, 247, 0.7);
                        border-radius: 5px;
                    }
                    QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
                        border: none;
                        background: none;
                    }
                """)

        # 创建内容容器
        content_widget = QWidget()
        layout = QVBoxLayout(content_widget)
        layout.setSpacing(20)
        layout.setContentsMargins(20, 20, 20, 20)

        # 顶部信息栏
        top_frame = QFrame()
        top_frame.setStyleSheet("""
                    QFrame {
                        background: qlineargradient(x1:0, y1:0, x2:1, y2:0, 
                                                  stop:0 #1a237e, stop:1 #0d47a1);
                        border-radius: 15px;
                        padding-left: 15px;  /* 左内边距 */
                        padding-right: 15px; /* 右内边距 */
                        padding-top: 0px;    /* 上内边距设为0 */
                        padding-bottom: 0px; /* 下内边距设为0 */
                    }
                """)
        top_layout = QHBoxLayout(top_frame)

        # 左侧：标题
        left_widget = QWidget()
        left_layout = QVBoxLayout(left_widget)

        title = QLabel("基于CNN的交通事故预测分析研究")
        title.setStyleSheet("color: white; font-size: 40px; font-weight: bold;")
        # 设置水平居中 + 垂直居中
        title.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        left_layout.addWidget(title)

        # 右侧：日期和时间
        datetime_widget = QWidget()
        datetime_layout = QHBoxLayout(datetime_widget)
        self.date_label = QLabel()
        self.time_label = QLabel()
        for label in [self.date_label, self.time_label]:
            label.setStyleSheet("color: #4fc3f7; font-size: 16px;")
            datetime_layout.addWidget(label)

        top_layout.addWidget(left_widget, 1)
        top_layout.addWidget(datetime_widget)

        layout.addWidget(top_frame,2)

        # 预测分析区域
        prediction_frame = QFrame()
        prediction_frame.setStyleSheet("""
                    QFrame {
                        background-color: rgba(13, 71, 161, 0.5);
                        border-radius: 15px;
                        padding: 15px;
                    }
                """)
        prediction_layout = QVBoxLayout(prediction_frame)

        # 预测标题
        pred_title = QLabel("事故风险预测")
        pred_title.setStyleSheet("color: white; font-size: 25px; font-weight: bold;")
        prediction_layout.addWidget(pred_title,2)

        # 预测数据展示
        pred_grid = QGridLayout()
        self.pred_value_labels = []
        self.pred_status_labels = []
        pred_items = [
            ("今日事故风险指数", "75/100", "高风险", "#ff5252"),
            ("事故多发区域", "东部沿海", "需注意", "#ffd740"),
            ("天气影响程度", "中等", "降雨", "#69f0ae"),
            ("交通拥堵指数", "85/100", "严重", "#ff5252")
        ]

        for i, (title, value, status, color) in enumerate(pred_items):
            card = QFrame()
            card.setStyleSheet(f"""
                        QFrame {{
                            background-color: rgba(13, 71, 161, 0.3);
                            border-radius: 10px;
                            padding: 10px;
                            border: 1px solid {color};
                        }}
                    """)
            card_layout = QVBoxLayout(card)

            title_label = QLabel(title)
            title_label.setStyleSheet("color: #4fc3f7; font-size: 14px;")

            value_label = QLabel(value)
            value_label.setStyleSheet(f"color: {color}; font-size: 20px; font-weight: bold;")

            status_label = QLabel(status)
            status_label.setStyleSheet(f"color: {color}; font-size: 12px;")

            card_layout.addWidget(title_label)
            card_layout.addWidget(value_label)
            card_layout.addWidget(status_label)

            self.pred_value_labels.append(value_label)
            self.pred_status_labels.append(status_label)

            pred_grid.addWidget(card, i // 2, i % 2)

        prediction_layout.addLayout(pred_grid,5)
        layout.addWidget(prediction_frame,3)

        # 设置滚动区域的内容
        scroll.setWidget(content_widget)

        # 创建主布局并添加滚动区域
        main_layout = QVBoxLayout(self)
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.addWidget(scroll)

    def update_datetime(self):
        """更新日期时间显示"""
        current = QDateTime.currentDateTime()
        self.date_label.setText(current.toString("yyyy年MM月dd日 dddd"))
        self.time_label.setText(current.toString("HH:mm:ss"))

    def update_prediction_data(self):
        """动态更新预测数据"""
        import random
        # 随机生成示例预测数据
        risk_index = random.randint(50, 100)
        risk_text = f"{risk_index}/100"
        risk_status = "高风险" if risk_index > 80 else "中风险" if risk_index > 60 else "低风险"
        risk_color = "#ff5252" if risk_index > 80 else "#ffd740" if risk_index > 60 else "#69f0ae"

        region = random.choice(["东部沿海", "西部山区", "南部平原", "北部高原"])
        region_status = "需注意" if region in ["东部沿海", "南部平原"] else "安全"
        region_color = risk_color

        weather = random.choice(["晴", "多云", "降雨", "大风"])
        weather_status = weather
        weather_color = "#69f0ae"

        congestion = random.randint(50, 100)
        congestion_text = f"{congestion}/100"
        congestion_status = "严重" if congestion > 80 else "拥堵" if congestion > 60 else "畅通"
        congestion_color = "#ff5252" if congestion > 80 else "#ffd740" if congestion > 60 else "#69f0ae"

        pred_data = [
            (risk_text, risk_status, risk_color),
            (region, region_status, region_color),
            (weather, weather_status, weather_color),
            (congestion_text, congestion_status, congestion_color)
        ]
        # 更新标签文本和样式
        for idx, (value, status, color) in enumerate(pred_data):
            self.pred_value_labels[idx].setText(value)
            self.pred_value_labels[idx].setStyleSheet(f"color: {color}; font-size: 20px; font-weight: bold;")
            self.pred_status_labels[idx].setText(status)
            self.pred_status_labels[idx].setStyleSheet(f"color: {color}; font-size: 12px;")


class FileListWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setup_ui()
        self.load_files()

    def setup_ui(self):
        layout = QVBoxLayout(self)

        # 文件列表
        self.file_list = QListWidget()
        self.file_list.setStyleSheet("""
            QListWidget {
                background-color: rgba(13, 71, 161, 0.3);
                border: 1px solid #4fc3f7;
                border-radius: 10px;
                padding: 10px;
                color: white;
            }
            QListWidget::item {
                padding: 5px;
                border-bottom: 1px solid #4fc3f7;
            }
            QListWidget::item:selected {
                background-color: rgba(79, 195, 247, 0.3);
            }
        """)
        layout.addWidget(self.file_list)

        # 按钮布局
        btn_layout = QHBoxLayout()

        self.delete_btn = GlowButton("删除文件")
        self.delete_btn.clicked.connect(self.delete_file)

        self.preview_btn = GlowButton("预览文件")
        self.preview_btn.clicked.connect(self.preview_file)

        btn_layout.addWidget(self.delete_btn)
        btn_layout.addWidget(self.preview_btn)
        layout.addLayout(btn_layout)

    def load_files(self):
        self.file_list.clear()
        upload_dir = os.path.join("traffic_analysis", "data", "uploaded_files")
        if os.path.exists(upload_dir):
            for file in os.listdir(upload_dir):
                self.file_list.addItem(file)

    def delete_file(self):
        current_item = self.file_list.currentItem()
        if current_item:
            file_name = current_item.text()
            file_path = os.path.join("traffic_analysis", "data", "uploaded_files", file_name)
            try:
                os.remove(file_path)
                self.load_files()  # 重新加载文件列表
            except Exception as e:
                QMessageBox.warning(None, "错误", f"删除文件失败: {str(e)}")

    def preview_file(self):
        current_item = self.file_list.currentItem()
        if current_item:
            file_name = current_item.text()
            file_path = os.path.join("traffic_analysis", "data", "uploaded_files", file_name)
            try:
                preview_dialog = FilePreviewDialog(file_path)
                preview_dialog.exec_()
            except Exception as e:
                QMessageBox.warning(None, "错误", f"预览文件失败: {str(e)}")


class FilePreviewDialog(QDialog):
    def __init__(self, file_path):
        super().__init__()
        self.file_path = file_path
        self.setup_ui()
        self.load_file()

    def setup_ui(self):
        self.setWindowTitle("文件预览")
        self.setMinimumSize(800, 600)

        layout = QVBoxLayout(self)

        # 数据表格
        self.table = QTableWidget()
        self.table.setStyleSheet("""
            QTableWidget {
                background-color: rgba(13, 71, 161, 0.3);
                color: white;
                border: 1px solid #4fc3f7;
                border-radius: 10px;
                gridline-color: #4fc3f7;
            }
            QHeaderView::section {
                background-color: #0d47a1;
                color: white;
                border: 1px solid #4fc3f7;
                padding: 5px;
            }
        """)
        layout.addWidget(self.table)

        # 分页控件
        page_layout = QHBoxLayout()

        self.prev_btn = GlowButton("上一页")
        self.prev_btn.clicked.connect(self.prev_page)

        self.page_label = QLabel("第 1 页")
        self.page_label.setStyleSheet("color: white;")

        self.next_btn = GlowButton("下一页")
        self.next_btn.clicked.connect(self.next_page)

        page_layout.addWidget(self.prev_btn)
        page_layout.addWidget(self.page_label)
        page_layout.addWidget(self.next_btn)

        layout.addLayout(page_layout)

        # 数据分析按钮
        analysis_layout = QHBoxLayout()

        self.basic_analysis_btn = GlowButton("基础统计分析")
        self.basic_analysis_btn.clicked.connect(self.show_basic_analysis)

        self.visual_analysis_btn = GlowButton("可视化分析")
        self.visual_analysis_btn.clicked.connect(self.show_visual_analysis)

        analysis_layout.addWidget(self.basic_analysis_btn)
        analysis_layout.addWidget(self.visual_analysis_btn)

        layout.addLayout(analysis_layout)

    def load_file(self):
        try:
            self.current_page = 0
            self.rows_per_page = 100

            if self.file_path.endswith('.csv'):
                self.df = pd.read_csv(self.file_path)
            elif self.file_path.endswith(('.xlsx', '.xls')):
                self.df = pd.read_excel(self.file_path)
            else:
                raise ValueError("不支持的文件格式")

            self.total_pages = len(self.df) // self.rows_per_page + 1
            self.show_page()

        except Exception as e:
            QMessageBox.warning(self, "错误", f"加载文件失败: {str(e)}")

    def show_page(self):
        start_idx = self.current_page * self.rows_per_page
        end_idx = start_idx + self.rows_per_page
        page_data = self.df.iloc[start_idx:end_idx]

        self.table.setRowCount(len(page_data))
        self.table.setColumnCount(len(self.df.columns))
        self.table.setHorizontalHeaderLabels(self.df.columns)

        for i, (_, row) in enumerate(page_data.iterrows()):
            for j, value in enumerate(row):
                item = QTableWidgetItem(str(value))
                self.table.setItem(i, j, item)

        self.page_label.setText(f"第 {self.current_page + 1} 页 / 共 {self.total_pages} 页")
        self.prev_btn.setEnabled(self.current_page > 0)
        self.next_btn.setEnabled(self.current_page < self.total_pages - 1)

    def prev_page(self):
        if self.current_page > 0:
            self.current_page -= 1
            self.show_page()

    def next_page(self):
        if self.current_page < self.total_pages - 1:
            self.current_page += 1
            self.show_page()

    def show_basic_analysis(self):
        try:
            analysis_dialog = QDialog(self)
            analysis_dialog.setWindowTitle("基础统计分析")
            analysis_dialog.setMinimumSize(600, 400)

            layout = QVBoxLayout(analysis_dialog)

            text_edit = QTextEdit()
            text_edit.setReadOnly(True)
            text_edit.setStyleSheet("""
                QTextEdit {
                    background-color: rgba(13, 71, 161, 0.3);
                    color: white;
                    border: 1px solid #4fc3f7;
                    border-radius: 10px;
                    padding: 10px;
                }
            """)

            # 基础统计信息
            analysis_text = "数据基本信息：\n"
            analysis_text += f"行数：{len(self.df)}\n"
            analysis_text += f"列数：{len(self.df.columns)}\n\n"

            analysis_text += "数值列统计：\n"
            numeric_cols = self.df.select_dtypes(include=[np.number]).columns
            for col in numeric_cols:
                analysis_text += f"\n{col}统计：\n"
                analysis_text += f"均值：{self.df[col].mean():.2f}\n"
                analysis_text += f"中位数：{self.df[col].median():.2f}\n"
                analysis_text += f"标准差：{self.df[col].std():.2f}\n"
                analysis_text += f"最小值：{self.df[col].min():.2f}\n"
                analysis_text += f"最大值：{self.df[col].max():.2f}\n"

            text_edit.setText(analysis_text)
            layout.addWidget(text_edit)

            analysis_dialog.exec_()

        except Exception as e:
            QMessageBox.warning(self, "错误", f"分析失败: {str(e)}")

    def show_visual_analysis(self):
        try:
            analysis_dialog = QDialog(self)
            analysis_dialog.setWindowTitle("可视化分析")
            analysis_dialog.setMinimumSize(800, 600)

            layout = QVBoxLayout(analysis_dialog)

            # 选择要可视化的列
            numeric_cols = self.df.select_dtypes(include=[np.number]).columns

            col_select_layout = QHBoxLayout()
            col_select_layout.addWidget(QLabel("选择要分析的列："))

            col_combo = QComboBox()
            col_combo.addItems(numeric_cols)
            col_select_layout.addWidget(col_combo)

            layout.addLayout(col_select_layout)

            # 创建图表部件
            plot_widget = pg.PlotWidget()
            plot_widget.setBackground('w')
            layout.addWidget(plot_widget)

            def update_plot():
                plot_widget.clear()
                selected_col = col_combo.currentText()
                data = self.df[selected_col].dropna()

                # 绘制直方图
                y, x = np.histogram(data, bins=50)
                curve = pg.PlotCurveItem(x, y, stepMode=True, fillLevel=0, brush=(0, 0, 255, 80))
                plot_widget.addItem(curve)
                plot_widget.setLabel('left', '频数')
                plot_widget.setLabel('bottom', selected_col)

            col_combo.currentTextChanged.connect(update_plot)
            update_plot()

            analysis_dialog.exec_()

        except Exception as e:
            QMessageBox.warning(self, "错误", f"可视化失败: {str(e)}")


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("交通数据分析系统")
        self.setMinimumSize(1400, 900)

        self.traffic_analyzer = TrafficAnalyzer()
        self.data_processor = DataProcessor()

        # 初始化文件路径变量
        self.feature_file_path = None
        self.ml_file_path = None
        self.nn_file_path = None
        self.comparison_data_path = None

        # 保存预览窗口的引用
        self.preview_dialogs = []

        # 设置文件管理目录
        self.file_manager_dir = os.path.join("traffic_analysis", "data", "uploaded_files")
        if not os.path.exists(self.file_manager_dir):
            os.makedirs(self.file_manager_dir)

        self.setup_ui()
        self.model_comparison = ModelComparison()
        self.setup_model_signals()

    def setup_model_signals(self):
        """
        设置模型训练过程中的信号连接
        """
        print("设置模型信号连接...")
        try:
            self.model_comparison.setup_model_signals(
                self.update_model_progress,
                self.update_results_table,
                self.show_final_comparison
            )
            print("模型信号连接成功")
        except Exception as e:
            print(f"错误：设置模型信号时发生错误 - {str(e)}")
            QMessageBox.critical(self, "错误", f"设置模型信号时发生错误：{str(e)}")
            import traceback
            traceback.print_exc()

    def setup_ui(self):
        # 创建中央部件和主布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QHBoxLayout(central_widget)

        # 创建导航侧边栏
        sidebar = QWidget()
        sidebar.setFixedWidth(250)
        sidebar.setStyleSheet("""
            QWidget {
                background-color: #0d1117;
                border-right: 2px solid #4fc3f7;
            }
        """)
        sidebar_layout = QVBoxLayout(sidebar)

        # 创建导航按钮
        nav_buttons = [
            ("首页", "icons/home.png"),
            ("数据清洗", "icons/clean.png"),
            ("特征提取", "icons/feature.png"),
            ("机器学习", "icons/ml.png"),
            ("神经网络", "icons/nn.png"),
            ("模型对比", "icons/compare.png"),  # 新增模型对比按钮
            ("数据可视化", "icons/visual.png"),
            ("文件管理", "icons/file.png")
        ]

        self.nav_buttons = []
        for text, icon in nav_buttons:
            btn = GlowButton(text)
            if icon:
                btn.setIcon(QIcon(icon))
                btn.setIconSize(QSize(24, 24))
            btn.clicked.connect(lambda checked, t=text: self.switch_page(t))
            sidebar_layout.addWidget(btn)
            self.nav_buttons.append(btn)

        sidebar_layout.addStretch()
        main_layout.addWidget(sidebar)

        # 创建堆叠部件
        self.stacked_widget = QStackedWidget()
        self.stacked_widget.setStyleSheet("""
            QStackedWidget {
                background-color: #0d1117;
            }
        """)
        main_layout.addWidget(self.stacked_widget)

        # 创建页面
        self.create_pages()

        # 设置动画计时器
        self.animation_timer = QTimer(self)
        self.animation_timer.timeout.connect(self.update_animations)
        self.animation_timer.start(50)

        # 默认显示首页
        self.switch_page("首页")

    def create_pages(self):
        # 创建仪表盘页面
        dashboard = DashboardWidget()
        dashboard.setObjectName("首页")
        self.stacked_widget.addWidget(dashboard)

        # 创建功能页面
        pages = {
            "数据清洗": self.create_data_cleaning_page(),
            "特征提取": self.create_feature_extraction_page(),
            "机器学习": self.create_ml_page(),
            "神经网络": self.create_nn_page(),
            "模型对比": self.create_model_comparison_page(),  # 新增模型对比页面
            "数据可视化": self.create_visualization_page(),
            "文件管理": self.create_file_management_page()
        }

        for name, page in pages.items():
            page.setObjectName(name)
            self.stacked_widget.addWidget(page)

    def create_data_cleaning_page(self):
        page = QWidget()
        layout = QVBoxLayout(page)

        # 添加标题
        title = QLabel("数据清洗")
        title.setFont(QFont("Microsoft YaHei", 18, QFont.Bold))
        title.setStyleSheet("color: #03a9f4;")
        title.setAlignment(Qt.AlignCenter)
        layout.addWidget(title)

        # 创建文件选择区域
        file_frame = QFrame()
        file_frame.setStyleSheet("""
            QFrame {
                background-color: rgba(13, 71, 161, 0.3);
                border-radius: 10px;
                border: 1px solid #4fc3f7;
                padding: 10px;
                margin-bottom: 10px;
            }
        """)
        file_layout = QHBoxLayout(file_frame)

        file_label = QLabel("当前文件：")
        file_label.setStyleSheet("color: white;")
        self.clean_file_label = QLabel("未选择文件")
        self.clean_file_label.setStyleSheet("color: #4fc3f7;")
        self.clean_select_btn = GlowButton("选择文件")
        self.clean_select_btn.clicked.connect(lambda: self.select_file_for_module("clean"))

        file_layout.addWidget(file_label)
        file_layout.addWidget(self.clean_file_label)
        file_layout.addWidget(self.clean_select_btn)
        layout.addWidget(file_frame)

        # 清洗方法选择区域
        clean_frame = QFrame()
        clean_frame.setStyleSheet("""
            QFrame {
                background-color: rgba(13, 71, 161, 0.5);
                border-radius: 15px;
                border: 2px solid #4fc3f7;
                padding: 20px;
            }
            QCheckBox {
                color: white;
                font-size: 14px;
                padding: 5px;
            }
            QCheckBox::indicator {
                width: 20px;
                height: 20px;
            }
            QCheckBox::indicator:unchecked {
                border: 2px solid #4fc3f7;
                background-color: transparent;
                border-radius: 4px;
            }
            QCheckBox::indicator:checked {
                border: 2px solid #4fc3f7;
                background-color: #03a9f4;
                border-radius: 4px;
            }
        """)
        clean_layout = QVBoxLayout(clean_frame)

        # 添加清洗选项（使用复选框）
        self.clean_options = {
            "数值处理": {
                "缺失值处理": QCheckBox("缺失值处理 - 处理数据中的空值和缺失值"),
                "异常值处理": QCheckBox("异常值处理 - 检测和处理异常数据"),
                "数据标准化": QCheckBox("数据标准化 - 将数据转换为标准格式（Z-score标准化）"),
                "重复值处理": QCheckBox("重复值处理 - 检测和删除重复数据")
            },
            "文本处理": {
                "标点符号处理": QCheckBox("标点符号处理 - 去除文本中的标点符号和特殊字符"),
                "大小写统一": QCheckBox("大小写统一 - 将文本统一转换为小写"),
                "停用词处理": QCheckBox("停用词处理 - 去除常见的停用词")
            }
        }

        # 添加数值处理选项组
        num_group = QGroupBox("数值数据处理")
        num_group.setStyleSheet("""
            QGroupBox {
                color: white;
                font-size: 14px;
                border: 1px solid #4fc3f7;
                border-radius: 5px;
                margin-top: 10px;
                padding-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px;
            }
        """)
        num_layout = QVBoxLayout()
        for checkbox in self.clean_options["数值处理"].values():
            num_layout.addWidget(checkbox)
        num_group.setLayout(num_layout)
        clean_layout.addWidget(num_group)

        # 添加文本处理选项组
        text_group = QGroupBox("文本数据处理")
        text_group.setStyleSheet(num_group.styleSheet())
        text_layout = QVBoxLayout()
        for checkbox in self.clean_options["文本处理"].values():
            text_layout.addWidget(checkbox)
        text_group.setLayout(text_layout)
        clean_layout.addWidget(text_group)

        layout.addWidget(clean_frame)

        # 添加开始清洗按钮
        start_clean_btn = GlowButton("开始清洗")
        start_clean_btn.clicked.connect(self.start_data_cleaning)
        layout.addWidget(start_clean_btn)

        # 添加进度条
        self.clean_progress = QProgressBar()
        self.clean_progress.setStyleSheet("""
            QProgressBar {
                border: 2px solid #4fc3f7;
                border-radius: 5px;
                text-align: center;
                background-color: #0d1117;
            }
            QProgressBar::chunk {
                background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1,
                                                stop:0 #1a237e, stop:1 #0d47a1);
            }
        """)
        self.clean_progress.hide()
        layout.addWidget(self.clean_progress)

        return page

    def start_data_cleaning(self):
        """开始数据清洗"""
        if not hasattr(self, 'clean_file_path') or not self.clean_file_path:
            QMessageBox.warning(self, "警告", "请先选择要清洗的文件！")
            return

        # 检查是否选择了清洗选项
        selected_options = {
            "数值处理": [name for name, checkbox in self.clean_options["数值处理"].items() if checkbox.isChecked()],
            "文本处理": [name for name, checkbox in self.clean_options["文本处理"].items() if checkbox.isChecked()]
        }

        if not any(selected_options.values()):
            QMessageBox.warning(self, "警告", "请至少选择一个清洗选项！")
            return

        self.clean_progress.show()
        self.clean_progress.setValue(0)

        try:
            # 首先检查文件大小
            file_size = os.path.getsize(self.clean_file_path) / (1024 * 1024)  # 转换为MB
            if file_size > 500:  # 如果文件大于500MB
                response = QMessageBox.question(
                    self,
                    "文件较大",
                    f"文件大小为 {file_size:.1f}MB，处理可能需要较长时间。是否继续？",
                    QMessageBox.Yes | QMessageBox.No
                )
                if response == QMessageBox.No:
                    self.clean_progress.hide()
                    return

            # 检测文件编码
            encodings = ['utf-8', 'gbk', 'gb2312', 'gb18030', 'big5', 'utf-16', 'iso-8859-1']
            detected_encoding = None

            try:
                import chardet
                with open(self.clean_file_path, 'rb') as file:
                    raw_data = file.read(1024)  # 只读取前1024字节来检测编码
                    result = chardet.detect(raw_data)
                    if result['encoding']:
                        detected_encoding = result['encoding']
                        encodings.insert(0, detected_encoding)
            except ImportError:
                pass  # 如果没有chardet，就使用预定义的编码列表

            # 分块读取文件
            chunk_size = 10000  # 每次读取10000行
            chunks = []
            total_chunks = 0

            # 尝试不同的编码读取文件
            success = False
            last_error = None

            for encoding in encodings:
                try:
                    if self.clean_file_path.endswith('.csv'):
                        # 首先计算总行数
                        with open(self.clean_file_path, 'r', encoding=encoding) as f:
                            total_chunks = sum(1 for _ in f) // chunk_size + 1

                        # 然后分块读取
                        chunks = []
                        current_progress = 0
                        progress_step = 80 / total_chunks

                        for chunk in pd.read_csv(self.clean_file_path, encoding=encoding, chunksize=chunk_size):
                            try:
                                # 处理每个数据块
                                chunk = self._process_data_chunk(chunk, selected_options)
                                chunks.append(chunk)

                                current_progress += progress_step
                                self.clean_progress.setValue(int(current_progress))
                                QApplication.processEvents()
                            except Exception as e:
                                QMessageBox.warning(self, "警告", f"处理数据块时出错：{str(e)}\n继续处理其他数据...")
                                continue

                        success = True
                        break
                    else:
                        # Excel文件使用默认编码
                        df = pd.read_excel(self.clean_file_path)
                        total_chunks = len(df) // chunk_size + 1

                        current_progress = 0
                        progress_step = 80 / total_chunks

                        for i in range(0, len(df), chunk_size):
                            try:
                                chunk = df.iloc[i:i + chunk_size].copy()
                                chunk = self._process_data_chunk(chunk, selected_options)
                                chunks.append(chunk)

                                current_progress += progress_step
                                self.clean_progress.setValue(int(current_progress))
                                QApplication.processEvents()
                            except Exception as e:
                                QMessageBox.warning(self, "警告", f"处理数据块时出错：{str(e)}\n继续处理其他数据...")
                                continue

                        success = True
                        break
                except Exception as e:
                    last_error = str(e)
                    continue

            if not success:
                raise Exception(f"无法读取文件，尝试了以下编码：{', '.join(encodings)}\n最后的错误：{last_error}")

            # 合并所有数据块
            try:
                final_df = pd.concat(chunks, ignore_index=True)
            except Exception as e:
                raise Exception(f"合并数据块失败：{str(e)}")

            # 保存处理后的文件
            self.clean_progress.setValue(90)

            # 修改保存文件的路径
            file_name = os.path.splitext(os.path.basename(self.clean_file_path))[0]
            file_ext = os.path.splitext(self.clean_file_path)[1]
            new_file_name = f"{file_name}_cleaned{file_ext}"
            save_path = os.path.join(self.file_manager_dir, new_file_name)
            save_path = self.get_unique_filename(save_path)

            # 分块保存，使用检测到的编码或UTF-8
            if file_ext == '.csv':
                final_df.to_csv(save_path, index=False, encoding=detected_encoding or 'utf-8-sig', chunksize=chunk_size)
            else:
                final_df.to_excel(save_path, index=False)

            self.clean_progress.setValue(100)

            # 显示成功消息
            success_message = (
                "数据清洗完成！\n"
                f"处理前行数：{len(final_df)}\n"
                f"使用的文件编码：{detected_encoding or 'utf-8'}\n"
                "已应用以下清洗方法：\n"
            )

            if selected_options["数值处理"]:
                success_message += "\n数值处理：\n- " + "\n- ".join(selected_options["数值处理"])
            if selected_options["文本处理"]:
                success_message += "\n文本处理：\n- " + "\n- ".join(selected_options["文本处理"])

            success_message += f"\n\n清洗后的文件已保存为：{new_file_name}"

            QMessageBox.information(self, "成功", success_message)
            self.refresh_file_list()

        except MemoryError:
            QMessageBox.critical(self, "错误", "内存不足，请尝试处理更小的文件或减少选择的清洗选项。")
        except Exception as e:
            QMessageBox.critical(self, "错误", f"数据清洗失败：{str(e)}")
        finally:
            self.clean_progress.hide()

    def _process_data_chunk(self, chunk, selected_options):
        """处理单个数据块"""
        try:
            # 识别列的数据类型
            numeric_cols = chunk.select_dtypes(include=[np.number]).columns
            text_cols = chunk.select_dtypes(include=['object']).columns

            # 数值数据处理
            if selected_options["数值处理"]:
                if "重复值处理" in selected_options["数值处理"]:
                    chunk = chunk.drop_duplicates()

                if "缺失值处理" in selected_options["数值处理"]:
                    for col in numeric_cols:
                        chunk[col] = chunk[col].fillna(chunk[col].mean())

                if "异常值处理" in selected_options["数值处理"]:
                    for col in numeric_cols:
                        Q1 = chunk[col].quantile(0.25)
                        Q3 = chunk[col].quantile(0.75)
                        IQR = Q3 - Q1
                        chunk[col] = chunk[col].clip(lower=Q1 - 1.5 * IQR, upper=Q3 + 1.5 * IQR)

                if "数据标准化" in selected_options["数值处理"]:
                    for col in numeric_cols:
                        mean = chunk[col].mean()
                        std = chunk[col].std()
                        if std != 0:
                            chunk[col] = (chunk[col] - mean) / std

            # 文本数据处理
            if selected_options["文本处理"]:
                for col in text_cols:
                    if "标点符号处理" in selected_options["文本处理"]:
                        chunk[col] = chunk[col].astype(str).apply(lambda x: re.sub(r'[^\w\s]', '', x))

                    if "大小写统一" in selected_options["文本处理"]:
                        chunk[col] = chunk[col].astype(str).str.lower()

                    if "停用词处理" in selected_options["文本处理"]:
                        try:
                            import jieba
                            stopwords = set(['的', '了', '和', '是', '就', '都', '而', '及', '与', '着'])
                            chunk[col] = chunk[col].astype(str).apply(
                                lambda x: ' '.join([word for word in jieba.cut(x) if word not in stopwords])
                            )
                        except ImportError:
                            pass  # 如果jieba未安装，跳过停用词处理

            return chunk
        except Exception as e:
            raise Exception(f"处理数据块时出错：{str(e)}")

    def get_unique_filename(self, base_path):
        """生成唯一的文件名，如果文件已存在则在后面加上数字"""
        if not os.path.exists(base_path):
            return base_path

        directory = os.path.dirname(base_path)
        filename = os.path.basename(base_path)
        name, ext = os.path.splitext(filename)

        counter = 1
        while os.path.exists(base_path):
            new_filename = f"{name}_{counter}{ext}"
            base_path = os.path.join(directory, new_filename)
            counter += 1

        return base_path

    def create_feature_extraction_page(self):
        page = QWidget()
        layout = QVBoxLayout(page)

        # 添加标题
        title = QLabel("特征提取")
        title.setFont(QFont("Microsoft YaHei", 18, QFont.Bold))
        title.setStyleSheet("color: #03a9f4;")
        title.setAlignment(Qt.AlignCenter)
        layout.addWidget(title)

        # 创建文件选择区域
        file_frame = QFrame()
        file_frame.setStyleSheet("""
            QFrame {
                background-color: rgba(13, 71, 161, 0.3);
                border-radius: 10px;
                border: 1px solid #4fc3f7;
                padding: 10px;
                margin-bottom: 10px;
            }
        """)
        file_layout = QHBoxLayout(file_frame)

        file_label = QLabel("当前文件：")
        file_label.setStyleSheet("color: white;")
        self.feature_file_label = QLabel("未选择文件")
        self.feature_file_label.setStyleSheet("color: #4fc3f7;")
        self.feature_select_btn = GlowButton("选择文件")
        self.feature_select_btn.clicked.connect(lambda: self.select_file_for_module("feature_extraction"))

        file_layout.addWidget(file_label)
        file_layout.addWidget(self.feature_file_label)
        file_layout.addWidget(self.feature_select_btn)
        layout.addWidget(file_frame)

        # 特征提取方法选择区域
        feature_frame = QFrame()
        feature_frame.setStyleSheet("""
            QFrame {
                background-color: rgba(13, 71, 161, 0.5);
                border-radius: 15px;
                border: 2px solid #4fc3f7;
                padding: 20px;
            }
            QCheckBox {
                color: white;
                font-size: 14px;
                padding: 5px;
            }
            QCheckBox::indicator {
                width: 20px;
                height: 20px;
            }
            QCheckBox::indicator:unchecked {
                border: 2px solid #4fc3f7;
                background-color: transparent;
                border-radius: 4px;
            }
            QCheckBox::indicator:checked {
                border: 2px solid #4fc3f7;
                background-color: #03a9f4;
                border-radius: 4px;
            }
        """)
        feature_layout = QVBoxLayout(feature_frame)

        # 添加特征提取选项
        self.feature_options = {
            "文本特征": {
                "词干提取": QCheckBox("词干提取 - 提取词语的词干形式"),
                "词形还原": QCheckBox("词形还原 - 将词语还原为基本形式"),
                "TF-IDF": QCheckBox("TF-IDF向量化 - 计算词语的TF-IDF特征"),
                "Word2Vec": QCheckBox("Word2Vec - 使用词嵌入模型提取特征")
            },
            "数值特征": {
                "统计特征": QCheckBox("统计特征 - 计算均值、方差等统计量"),
                "降维技术": QCheckBox("降维技术 - 使用PCA、LDA等降维方法"),
                "数据变换": QCheckBox("数据变换 - 多项式特征、离散化等"),
                "深度特征": QCheckBox("深度特征 - 使用CNN、RNN等提取特征"),
                "特征选择": QCheckBox("特征选择 - 使用过滤法、包装法等选择特征")
            }
        }

        # 添加文本特征提取选项组
        text_group = QGroupBox("文本特征提取")
        text_group.setStyleSheet("""
            QGroupBox {
                color: white;
                font-size: 14px;
                border: 1px solid #4fc3f7;
                border-radius: 5px;
                margin-top: 10px;
                padding-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px;
            }
        """)
        text_layout = QVBoxLayout()
        for checkbox in self.feature_options["文本特征"].values():
            text_layout.addWidget(checkbox)
        text_group.setLayout(text_layout)
        feature_layout.addWidget(text_group)

        # 添加数值特征提取选项组
        num_group = QGroupBox("数值特征提取")
        num_group.setStyleSheet(text_group.styleSheet())
        num_layout = QVBoxLayout()
        for checkbox in self.feature_options["数值特征"].values():
            num_layout.addWidget(checkbox)
        num_group.setLayout(num_layout)
        feature_layout.addWidget(num_group)

        layout.addWidget(feature_frame)

        # 添加开始提取按钮
        start_extract_btn = GlowButton("开始提取")
        start_extract_btn.clicked.connect(self.start_feature_extraction)
        layout.addWidget(start_extract_btn)

        # 添加进度条
        self.feature_progress = QProgressBar()
        self.feature_progress.setStyleSheet("""
            QProgressBar {
                border: 2px solid #4fc3f7;
                border-radius: 5px;
                text-align: center;
                background-color: #0d1117;
            }
            QProgressBar::chunk {
                background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1,
                                                stop:0 #1a237e, stop:1 #0d47a1);
            }
        """)
        self.feature_progress.hide()
        layout.addWidget(self.feature_progress)

        return page

    def create_ml_page(self):
        ml_page = QWidget()
        layout = QVBoxLayout(ml_page)

        # 创建数据配置组
        data_group = QGroupBox("数据配置")
        data_layout = QGridLayout()

        # 文件选择区域
        file_select_frame = QFrame()
        file_select_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        file_select_layout = QVBoxLayout(file_select_frame)

        # 文件选择控件
        file_input_layout = QHBoxLayout()
        self.ml_file_path = QLineEdit()
        self.ml_file_path.setPlaceholderText("选择数据文件...")
        self.ml_file_path.setReadOnly(True)
        select_file_btn = QPushButton("浏览")
        select_file_btn.clicked.connect(lambda: self.select_file_for_module("ml"))
        file_input_layout.addWidget(self.ml_file_path)
        file_input_layout.addWidget(select_file_btn)

        # 文件名显示标签
        self.ml_file_label = QLabel("未选择文件")
        self.ml_file_label.setStyleSheet("""
            QLabel {
                color: #4fc3f7;
                padding: 5px;
                font-weight: bold;
            }
        """)

        file_select_layout.addLayout(file_input_layout)
        file_select_layout.addWidget(self.ml_file_label)

        # 特征和目标选择
        self.feature_cols = QListWidget()
        self.feature_cols.setSelectionMode(QListWidget.MultiSelection)
        self.target_col = QComboBox()

        # 训练参数配置
        self.test_size = QDoubleSpinBox()
        self.test_size.setRange(0.1, 0.5)
        self.test_size.setValue(0.2)
        self.test_size.setSingleStep(0.1)

        self.random_seed = QSpinBox()
        self.random_seed.setRange(1, 1000)
        self.random_seed.setValue(42)

        # 添加到数据布局
        data_layout.addWidget(QLabel("数据文件:"), 0, 0)
        data_layout.addWidget(file_select_frame, 0, 1, 1, 2)
        data_layout.addWidget(QLabel("特征列:"), 2, 0)
        data_layout.addWidget(self.feature_cols, 2, 1, 1, 2)
        data_layout.addWidget(QLabel("目标列:"), 3, 0)
        data_layout.addWidget(self.target_col, 3, 1, 1, 2)
        data_layout.addWidget(QLabel("测试集比例:"), 4, 0)
        data_layout.addWidget(self.test_size, 4, 1)
        data_layout.addWidget(QLabel("随机种子:"), 5, 0)
        data_layout.addWidget(self.random_seed, 5, 1)
        data_group.setLayout(data_layout)

        # 创建模型选择组：监督学习
        sup_group = QGroupBox("监督学习")
        sup_layout = QVBoxLayout()
        self.sup_checks = {
            "线性回归": QCheckBox("线性回归"),
            "逻辑回归": QCheckBox("逻辑回归"),
            "HMM": QCheckBox("HMM")
        }
        for cb in self.sup_checks.values():
            sup_layout.addWidget(cb)
        sup_group.setLayout(sup_layout)
        layout.addWidget(sup_group)

        # 创建模型选择组：无监督学习
        unsup_group = QGroupBox("无监督学习")
        unsup_layout = QVBoxLayout()
        self.unsup_checks = {
            "KMeans": QCheckBox("KMeans 聚类"),
            "PCA": QCheckBox("PCA 降维")
        }
        for cb in self.unsup_checks.values():
            unsup_layout.addWidget(cb)
        unsup_group.setLayout(unsup_layout)
        layout.addWidget(unsup_group)

        # 创建模型选择组：强化学习
        rl_group = QGroupBox("强化学习")
        rl_layout = QVBoxLayout()
        self.rl_checks = {
            "Q-Learning": QCheckBox("Q-Learning"),
            "SARSA": QCheckBox("SARSA")
        }
        for cb in self.rl_checks.values():
            rl_layout.addWidget(cb)
        rl_group.setLayout(rl_layout)
        layout.addWidget(rl_group)

        # 创建训练控制组
        train_group = QGroupBox("训练控制")
        train_layout = QVBoxLayout()

        self.ml_progress = QProgressBar()
        start_train_btn = QPushButton("开始训练")
        start_train_btn.clicked.connect(self.start_ml_training)

        train_layout.addWidget(self.ml_progress)
        train_layout.addWidget(start_train_btn)
        train_group.setLayout(train_layout)

        # 创建结果显示组
        results_group = QGroupBox("训练结果")
        results_layout = QVBoxLayout()

        self.ml_results = QTextEdit()
        self.ml_results.setReadOnly(True)
        results_layout.addWidget(self.ml_results)
        results_group.setLayout(results_layout)

        # 添加所有组件到主布局
        layout.addWidget(data_group)
        layout.addWidget(train_group)
        layout.addWidget(results_group)

        return ml_page

    def create_nn_page(self):
        nn_page = QWidget()
        layout = QVBoxLayout(nn_page)

        # 创建数据配置组
        data_group = QGroupBox("数据配置")
        data_layout = QGridLayout()

        # 文件选择区域
        file_select_frame = QFrame()
        file_select_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        file_select_layout = QVBoxLayout(file_select_frame)

        # 文件选择控件
        file_input_layout = QHBoxLayout()
        self.nn_file_path = QLineEdit()
        self.nn_file_path.setPlaceholderText("选择数据文件...")
        self.nn_file_path.setReadOnly(True)
        select_file_btn = QPushButton("浏览")
        select_file_btn.clicked.connect(lambda: self.select_file_for_module("nn"))
        file_input_layout.addWidget(self.nn_file_path)
        file_input_layout.addWidget(select_file_btn)

        # 文件名显示标签
        self.nn_file_label = QLabel("未选择文件")
        self.nn_file_label.setStyleSheet("""
            QLabel {
                color: #4fc3f7;
                padding: 5px;
                font-weight: bold;
            }
        """)

        file_select_layout.addLayout(file_input_layout)
        file_select_layout.addWidget(self.nn_file_label)

        # 特征和目标选择
        self.nn_feature_cols = QListWidget()
        self.nn_feature_cols.setSelectionMode(QListWidget.MultiSelection)
        self.nn_target_col = QComboBox()

        # 训练参数配置
        self.nn_test_size = QDoubleSpinBox()
        self.nn_test_size.setRange(0.1, 0.5)
        self.nn_test_size.setValue(0.2)
        self.nn_test_size.setSingleStep(0.1)

        self.nn_random_seed = QSpinBox()
        self.nn_random_seed.setRange(1, 1000)
        self.nn_random_seed.setValue(42)

        self.timesteps = QSpinBox()
        self.timesteps.setRange(1, 100)
        self.timesteps.setValue(30)

        # 添加到数据布局
        data_layout.addWidget(QLabel("数据文件:"), 0, 0)
        data_layout.addWidget(file_select_frame, 0, 1, 1, 2)
        data_layout.addWidget(QLabel("特征列:"), 2, 0)
        data_layout.addWidget(self.nn_feature_cols, 2, 1, 1, 2)
        data_layout.addWidget(QLabel("目标列:"), 3, 0)
        data_layout.addWidget(self.nn_target_col, 3, 1, 1, 2)
        data_layout.addWidget(QLabel("测试集比例:"), 4, 0)
        data_layout.addWidget(self.nn_test_size, 4, 1)
        data_layout.addWidget(QLabel("随机种子:"), 5, 0)
        data_layout.addWidget(self.nn_random_seed, 5, 1)
        data_layout.addWidget(QLabel("时间步长:"), 6, 0)
        data_layout.addWidget(self.timesteps, 6, 1)
        data_group.setLayout(data_layout)

        # 创建模型选择组（多选所有神经网络模型）
        model_group = QGroupBox("模型选择")
        model_layout = QVBoxLayout()
        self.nn_model_checks = {
            "RNN": QCheckBox("RNN"),
            "BiLSTM": QCheckBox("BiLSTM")
        }
        for cb in self.nn_model_checks.values():
            model_layout.addWidget(cb)
        model_group.setLayout(model_layout)

        # 创建训练控制组
        train_group = QGroupBox("训练控制")
        train_layout = QVBoxLayout()

        self.nn_progress = QProgressBar()
        start_train_btn = QPushButton("开始训练")
        start_train_btn.clicked.connect(self.start_nn_training)

        train_layout.addWidget(self.nn_progress)
        train_layout.addWidget(start_train_btn)
        train_group.setLayout(train_layout)

        # 创建结果显示组
        results_group = QGroupBox("训练结果")
        results_layout = QVBoxLayout()

        self.nn_results = QTextEdit()
        self.nn_results.setReadOnly(True)
        results_layout.addWidget(self.nn_results)
        results_group.setLayout(results_layout)

        # 添加所有组件到主布局
        layout.addWidget(data_group)
        layout.addWidget(model_group)
        layout.addWidget(train_group)
        layout.addWidget(results_group)

        return nn_page

    def handle_error(self, error_message):
        """处理错误"""
        self.chat_history.append(f"<p style='color: red;'>错误: {error_message}</p>")
        self.chat_history.verticalScrollBar().setValue(
            self.chat_history.verticalScrollBar().maximum()
        )
        self.chat_input.setEnabled(True)

    def create_visualization_page(self):
        page = QWidget()
        main_layout = QVBoxLayout()
        main_layout.setSpacing(10)
        main_layout.setContentsMargins(10, 10, 10, 10)
        page.setLayout(main_layout)

        # 添加标题
        title = QLabel("数据可视化")
        title.setFont(QFont("Microsoft YaHei", 18, QFont.Bold))
        title.setStyleSheet("color: #03a9f4;")
        title.setAlignment(Qt.AlignCenter)
        main_layout.addWidget(title)

        # 文件选择区域
        file_frame = QFrame()
        file_frame.setStyleSheet("""
            QFrame {
                background-color: rgba(13, 71, 161, 0.3);
                border-radius: 10px;
                border: 1px solid #4fc3f7;
            }
        """)
        file_layout = QHBoxLayout(file_frame)
        file_layout.setContentsMargins(10, 10, 10, 10)

        file_label = QLabel("当前文件：")
        file_label.setStyleSheet("color: white;")
        self.viz_file_label = QLabel("未选择文件")
        self.viz_file_label.setStyleSheet("color: #4fc3f7;")

        select_file_btn = QPushButton("选择文件")
        select_file_btn.setFixedSize(100, 30)
        select_file_btn.setStyleSheet("""
            QPushButton {
                background-color: #1a237e;
                color: white;
                border-radius: 5px;
                border: 1px solid #4fc3f7;
            }
            QPushButton:hover {
                background-color: #0d47a1;
            }
        """)
        select_file_btn.clicked.connect(lambda: self.select_file_for_module("visualization"))

        file_layout.addWidget(file_label)
        file_layout.addWidget(self.viz_file_label, 1)
        file_layout.addWidget(select_file_btn)
        main_layout.addWidget(file_frame)

        # 数据选择区域
        data_frame = QFrame()
        data_frame.setStyleSheet(file_frame.styleSheet())
        data_layout = QHBoxLayout(data_frame)
        data_layout.setContentsMargins(10, 10, 10, 10)

        # 数据类型选择
        type_label = QLabel("数据类型：")
        type_label.setStyleSheet("color: white;")
        self.data_type_combo = QComboBox()
        self.data_type_combo.addItems(["数值型", "文本型", "时间型"])
        self.data_type_combo.setFixedWidth(100)
        self.data_type_combo.setStyleSheet("""
            QComboBox {
                background-color: #1e2329;
                color: white;
                border: 1px solid #4fc3f7;
                border-radius: 5px;
                padding: 5px;
            }
        """)
        self.data_type_combo.currentTextChanged.connect(self.update_column_by_type)

        column_label = QLabel("选择数据列：")
        column_label.setStyleSheet("color: white;")
        self.column_combo = QComboBox()
        self.column_combo.setFixedWidth(200)
        self.column_combo.setStyleSheet(self.data_type_combo.styleSheet())

        data_layout.addWidget(type_label)
        data_layout.addWidget(self.data_type_combo)
        data_layout.addWidget(column_label)
        data_layout.addWidget(self.column_combo)
        data_layout.addStretch()
        main_layout.addWidget(data_frame)

        # 分析按钮区域
        button_frame = QFrame()
        button_frame.setStyleSheet(file_frame.styleSheet())
        button_grid = QGridLayout(button_frame)
        button_grid.setContentsMargins(10, 10, 10, 10)
        button_grid.setSpacing(10)

        viz_buttons = [
            ("时间序列分析", 0, 0),
            ("空间分布分析", 0, 1),
            ("相关性分析", 1, 0),
            ("整体数据分析", 1, 1)
        ]

        for name, row, col in viz_buttons:
            btn = QPushButton(name)
            btn.setFixedSize(180, 40)
            btn.setStyleSheet("""
                QPushButton {
                    background-color: #1a237e;
                    color: white;
                    border-radius: 5px;
                    border: 1px solid #4fc3f7;
                }
                QPushButton:hover {
                    background-color: #0d47a1;
                }
            """)
            btn.clicked.connect(lambda checked, n=name: self.show_visualization(n))
            button_grid.addWidget(btn, row, col)

        main_layout.addWidget(button_frame)

        # 图表显示区域
        chart_frame = QFrame()
        chart_frame.setStyleSheet(file_frame.styleSheet())
        chart_layout = QVBoxLayout(chart_frame)
        chart_layout.setContentsMargins(10, 10, 10, 10)

        self.plot_widget = pg.PlotWidget()
        self.plot_widget.setBackground('#1e2329')
        self.plot_widget.setMinimumHeight(400)
        self.plot_widget.setLabel('left', '数值')
        self.plot_widget.setLabel('bottom', '数据点')
        chart_layout.addWidget(self.plot_widget)

        main_layout.addWidget(chart_frame)
        main_layout.setStretch(4, 1)  # 让图表区域占据更多空间

        return page

    def update_column_by_type(self):
        """根据选择的数据类型更新列选择下拉框"""
        if not hasattr(self, 'current_df'):
            return

        data_type = self.data_type_combo.currentText()
        self.column_combo.clear()

        try:
            if data_type == "数值型":
                # 选择数值类型的列
                numeric_cols = self.current_df.select_dtypes(include=[np.number]).columns
                self.column_combo.addItems(numeric_cols)
            elif data_type == "文本型":
                # 选择对象（字符串）类型的列
                text_cols = self.current_df.select_dtypes(include=['object']).columns
                self.column_combo.addItems(text_cols)
            elif data_type == "时间型":
                # 尝试识别时间类型的列
                time_cols = []
                for col in self.current_df.columns:
                    try:
                        pd.to_datetime(self.current_df[col])
                        time_cols.append(col)
                    except:
                        continue
                self.column_combo.addItems(time_cols)
        except Exception as e:
            QMessageBox.warning(self, "错误", f"更新列选择失败：{str(e)}")

    def show_visualization(self, viz_type):
        """显示不同类型的可视化"""
        try:
            if not hasattr(self, 'current_df'):
                QMessageBox.warning(self, "警告", "请先选择文件！")
                return

            if viz_type == "整体数据分析":
                self.show_comprehensive_analysis()
                return

            selected_column = self.column_combo.currentText()
            if not selected_column:
                QMessageBox.warning(self, "警告", "请选择数据列！")
                return

            data = self.current_df[selected_column]
            data_type = self.data_type_combo.currentText()

            self.plot_widget.clear()

            if data_type == "数值型":
                if viz_type == "时间序列分析":
                    # 数值型数据的时间序列图
                    x = np.arange(len(data))
                    self.plot_widget.plot(x, data.values, pen=(0, 162, 232))
                    self.plot_widget.setLabel('left', selected_column)
                    self.plot_widget.setLabel('bottom', '时间')

                elif viz_type == "空间分布分析":
                    # 数值型数据的分布图
                    y, x = np.histogram(data.dropna(), bins=50)
                    self.plot_widget.plot(x, y, stepMode=True, fillLevel=0, brush=(0, 162, 232, 100))
                    self.plot_widget.setLabel('left', '频数')
                    self.plot_widget.setLabel('bottom', selected_column)

                elif viz_type == "相关性分析":
                    # 数值型数据的相关性分析
                    numeric_cols = self.current_df.select_dtypes(include=[np.number]).columns
                    correlations = self.current_df[numeric_cols].corr()[selected_column].sort_values()
                    x = np.arange(len(correlations))
                    self.plot_widget.plot(x, correlations.values, pen=(0, 162, 232))
                    self.plot_widget.setLabel('left', '相关系数')
                    self.plot_widget.setLabel('bottom', '特征')

            elif data_type == "文本型":
                if viz_type == "空间分布分析":
                    # 文本型数据的频率分布
                    value_counts = data.value_counts()
                    x = np.arange(len(value_counts))
                    self.plot_widget.plot(x, value_counts.values, pen=(0, 162, 232))
                    self.plot_widget.setLabel('left', '频数')
                    self.plot_widget.setLabel('bottom', '类别')

            elif data_type == "时间型":
                if viz_type == "时间序列分析":
                    # 转换为时间序列
                    time_data = pd.to_datetime(data)
                    time_values = (time_data - time_data.min()).dt.total_seconds()
                    self.plot_widget.plot(time_values, np.arange(len(time_values)), pen=(0, 162, 232))
                    self.plot_widget.setLabel('left', '累计数量')
                    self.plot_widget.setLabel('bottom', '时间')

        except Exception as e:
            QMessageBox.critical(self, "错误", f"显示可视化失败：{str(e)}")

    def show_comprehensive_analysis(self):
        """显示整体数据分析结果"""
        try:
            if not hasattr(self, 'current_df'):
                QMessageBox.warning(self, "警告", "请先选择文件！")
                return

            # 创建分析结果窗口
            analysis_dialog = QDialog(self)
            analysis_dialog.setWindowTitle("整体数据分析")
            analysis_dialog.resize(800, 600)

            layout = QVBoxLayout(analysis_dialog)

            # 创建文本显示区域
            text_edit = QTextEdit()
            text_edit.setReadOnly(True)
            text_edit.setStyleSheet("""
                QTextEdit {
                    background-color: #1e2329;
                    color: white;
                    border: 1px solid #4fc3f7;
                    border-radius: 5px;
                    padding: 10px;
                }
            """)

            # 生成分析报告
            report = "数据整体分析报告\n\n"

            # 1. 基本信息
            report += "1. 基本信息：\n"
            report += f"- 总行数：{len(self.current_df)}\n"
            report += f"- 总列数：{len(self.current_df.columns)}\n"
            report += f"- 数据列：{', '.join(self.current_df.columns)}\n\n"

            # 2. 数据类型分析
            report += "2. 数据类型分析：\n"
            for col in self.current_df.columns:
                report += f"- {col}: {self.current_df[col].dtype}\n"
            report += "\n"

            # 3. 数值型数据分析
            numeric_cols = self.current_df.select_dtypes(include=[np.number]).columns
            if len(numeric_cols) > 0:
                report += "3. 数值型数据分析：\n"
                for col in numeric_cols:
                    report += f"\n{col} 的统计信息：\n"
                    report += f"- 均值：{self.current_df[col].mean():.2f}\n"
                    report += f"- 中位数：{self.current_df[col].median():.2f}\n"
                    report += f"- 标准差：{self.current_df[col].std():.2f}\n"
                    report += f"- 最小值：{self.current_df[col].min():.2f}\n"
                    report += f"- 最大值：{self.current_df[col].max():.2f}\n"
                report += "\n"

            # 4. 文本型数据分析
            text_cols = self.current_df.select_dtypes(include=['object']).columns
            if len(text_cols) > 0:
                report += "4. 文本型数据分析：\n"
                for col in text_cols:
                    report += f"\n{col} 的统计信息：\n"
                    value_counts = self.current_df[col].value_counts()
                    report += f"- 唯一值数量：{len(value_counts)}\n"
                    report += "- 前5个最常见值：\n"
                    for val, count in value_counts.head().items():
                        report += f"  * {val}: {count}次\n"
                report += "\n"

            # 5. 缺失值分析
            report += "5. 缺失值分析：\n"
            missing_data = self.current_df.isnull().sum()
            for col, count in missing_data.items():
                if count > 0:
                    report += f"- {col}: {count}个缺失值 ({count / len(self.current_df) * 100:.2f}%)\n"

            text_edit.setText(report)
            layout.addWidget(text_edit)

            # 添加关闭按钮
            close_btn = QPushButton("关闭")
            close_btn.setStyleSheet("""
                QPushButton {
                    background-color: #1a237e;
                    color: white;
                    border-radius: 5px;
                    border: 1px solid #4fc3f7;
                    padding: 5px 20px;
                }
                QPushButton:hover {
                    background-color: #0d47a1;
                }
            """)
            close_btn.clicked.connect(analysis_dialog.close)
            layout.addWidget(close_btn)

            analysis_dialog.exec_()

        except Exception as e:
            QMessageBox.critical(self, "错误", f"生成分析报告失败：{str(e)}")

    def create_file_management_page(self):
        page = QWidget()
        layout = QVBoxLayout()
        page.setLayout(layout)

        # 标题
        title = QLabel("文件管理")
        title.setStyleSheet("font-size: 24px; color: #4fc3f7; margin: 10px;")
        layout.addWidget(title)

        # 创建按钮区域
        button_frame = QFrame()
        button_frame.setStyleSheet("QFrame { background-color: #1e2329; border-radius: 10px; padding: 10px; }")
        button_layout = QHBoxLayout()
        button_frame.setLayout(button_layout)

        upload_btn = GlowButton("上传文件")
        upload_btn.clicked.connect(self.upload_file)

        import_db_btn = GlowButton("从数据库导入")
        import_db_btn.clicked.connect(self.show_database_import_dialog)

        button_layout.addWidget(upload_btn)
        button_layout.addWidget(import_db_btn)
        layout.addWidget(button_frame)

        # 文件列表区域
        self.file_list_widget = QTableWidget()
        self.file_list_widget.setColumnCount(4)
        self.file_list_widget.setHorizontalHeaderLabels(["文件名", "大小", "上传时间", "操作"])
        self.file_list_widget.setStyleSheet("""
            QTableWidget {
                background-color: #1e2329;
                border-radius: 10px;
                color: #ffffff;
                gridline-color: #2c3e50;
            }
            QHeaderView::section {
                background-color: #2c3e50;
                color: #ffffff;
                border: none;
                padding: 5px;
            }
        """)
        self.file_list_widget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        layout.addWidget(self.file_list_widget)

        # 刷新文件列表
        self.refresh_file_list()

        return page

    def show_database_import_dialog(self):
        """显示数据库导入对话框"""
        try:
            # 创建对话框
            dialog = QDialog(self)
            dialog.setWindowTitle("从数据库导入")
            dialog.setMinimumWidth(800)
            dialog.setMinimumHeight(600)
            layout = QVBoxLayout(dialog)

            # 添加数据库连接状态标签
            status_label = QLabel("正在连接数据库...")
            status_label.setStyleSheet("color: #4fc3f7;")
            layout.addWidget(status_label)

            # 创建表格选择列表
            table_list = QListWidget()
            table_list.setStyleSheet("""
                QListWidget {
                    background-color: #1e2329;
                    color: white;
                    border: 1px solid #4fc3f7;
                    border-radius: 5px;
                }
                QListWidget::item {
                    padding: 5px;
                }
                QListWidget::item:selected {
                    background-color: #0d47a1;
                }
            """)
            layout.addWidget(table_list)

            # 创建预览区域
            preview_table = QTableWidget()
            preview_table.setStyleSheet("""
                QTableWidget {
                    background-color: #1e2329;
                    color: white;
                    border: 1px solid #4fc3f7;
                    border-radius: 5px;
                }
                QHeaderView::section {
                    background-color: #0d47a1;
                    color: white;
                    border: 1px solid #4fc3f7;
                }
            """)
            layout.addWidget(preview_table)

            # 添加按钮
            button_layout = QHBoxLayout()
            import_btn = QPushButton("导入选中表")
            import_btn.setEnabled(False)
            import_btn.setStyleSheet("""
                QPushButton {
                    background-color: #1a237e;
                    color: white;
                    border-radius: 5px;
                    border: 1px solid #4fc3f7;
                    padding: 5px 20px;
                }
                QPushButton:hover {
                    background-color: #0d47a1;
                }
                QPushButton:disabled {
                    background-color: #424242;
                    border: 1px solid #757575;
                }
            """)

            cancel_btn = QPushButton("取消")
            cancel_btn.setStyleSheet(import_btn.styleSheet())

            button_layout.addWidget(import_btn)
            button_layout.addWidget(cancel_btn)
            layout.addLayout(button_layout)

            # 创建数据库连接线程
            class DatabaseThread(QThread):
                connection_status = pyqtSignal(bool, str)
                tables_loaded = pyqtSignal(list)
                preview_data_loaded = pyqtSignal(list, list)  # headers, data
                import_completed = pyqtSignal(bool, str)

                def __init__(self):
                    super().__init__()
                    self.action = None
                    self.table_name = None
                    self.pool = None

                def run(self):
                    if self.action == "connect":
                        self.connect_to_database()
                    elif self.action == "preview":
                        self.preview_table()
                    elif self.action == "import":
                        self.import_table()

                def connect_to_database(self):
                    try:
                        from dbutils.pooled_db import PooledDB
                        import mysql.connector

                        self.pool = PooledDB(
                            mysql.connector,
                            mincached=2,
                            maxcached=10,
                            host="121.199.60.228",
                            user="root",
                            password="root",
                            database="traffic"
                        )

                        # 获取所有表名
                        conn = self.pool.connection()
                        cursor = conn.cursor()
                        cursor.execute("SHOW TABLES")
                        tables = [table[0] for table in cursor.fetchall()]
                        cursor.close()
                        conn.close()

                        self.connection_status.emit(True, "数据库连接成功")
                        self.tables_loaded.emit(tables)
                    except Exception as e:
                        self.connection_status.emit(False, f"数据库连接失败: {str(e)}")

                def preview_table(self):
                    try:
                        conn = self.pool.connection()
                        cursor = conn.cursor()

                        # 获取表结构
                        cursor.execute(f"DESCRIBE {self.table_name}")
                        columns = [col[0] for col in cursor.fetchall()]

                        # 获取预览数据
                        cursor.execute(f"SELECT * FROM {self.table_name} LIMIT 100")
                        data = cursor.fetchall()

                        cursor.close()
                        conn.close()

                        self.preview_data_loaded.emit(columns, list(data))
                    except Exception as e:
                        self.connection_status.emit(False, f"预览数据失败: {str(e)}")

                def import_table(self):
                    try:
                        conn = self.pool.connection()
                        cursor = conn.cursor()

                        # 获取所有数据
                        cursor.execute(f"SELECT * FROM {self.table_name}")
                        data = cursor.fetchall()

                        # 获取列名
                        cursor.execute(f"DESCRIBE {self.table_name}")
                        columns = [col[0] for col in cursor.fetchall()]

                        cursor.close()
                        conn.close()

                        # 创建DataFrame并保存为CSV
                        import pandas as pd
                        df = pd.DataFrame(data, columns=columns)

                        # 生成文件名
                        file_name = f"{self.table_name}_database.csv"
                        save_path = os.path.join("traffic_analysis", "data", "uploaded_files", file_name)

                        # 获取唯一的文件名
                        counter = 1
                        while os.path.exists(save_path):
                            file_name = f"{self.table_name}_database_{counter}.csv"
                            save_path = os.path.join("traffic_analysis", "data", "uploaded_files", file_name)
                            counter += 1

                        # 保存文件
                        df.to_csv(save_path, index=False, encoding='utf-8-sig')

                        self.import_completed.emit(True, file_name)
                    except Exception as e:
                        self.import_completed.emit(False, str(e))

            # 创建数据库线程实例
            db_thread = DatabaseThread()

            # 连接信号槽
            def update_connection_status(success, message):
                status_label.setText(message)
                if not success:
                    table_list.clear()
                    preview_table.setRowCount(0)
                    preview_table.setColumnCount(0)
                    import_btn.setEnabled(False)

            def update_table_list(tables):
                table_list.clear()
                table_list.addItems(tables)

            def show_preview_data(headers, data):
                preview_table.setColumnCount(len(headers))
                preview_table.setHorizontalHeaderLabels(headers)
                preview_table.setRowCount(len(data))

                for i, row in enumerate(data):
                    for j, value in enumerate(row):
                        item = QTableWidgetItem(str(value))
                        preview_table.setItem(i, j, item)

                import_btn.setEnabled(True)

            def handle_import_completed(success, message):
                if success:
                    QMessageBox.information(dialog, "成功", f"数据已成功导入到文件: {message}")
                    self.refresh_file_list()
                    dialog.accept()
                else:
                    QMessageBox.critical(dialog, "错误", f"导入失败: {message}")

            def handle_table_selection():
                if table_list.currentItem():
                    db_thread.table_name = table_list.currentItem().text()
                    db_thread.action = "preview"
                    db_thread.start()

            # 连接信号
            db_thread.connection_status.connect(update_connection_status)
            db_thread.tables_loaded.connect(update_table_list)
            db_thread.preview_data_loaded.connect(show_preview_data)
            db_thread.import_completed.connect(handle_import_completed)
            table_list.itemClicked.connect(handle_table_selection)

            # 连接按钮事件
            import_btn.clicked.connect(lambda: (
                setattr(db_thread, 'action', 'import'),
                db_thread.start()
            ))
            cancel_btn.clicked.connect(dialog.reject)

            # 开始连接数据库
            db_thread.action = "connect"
            db_thread.start()

            # 显示对话框
            dialog.exec_()

        except Exception as e:
            QMessageBox.critical(self, "错误", f"显示数据库导入对话框失败: {str(e)}")

    def switch_page(self, page_name):
        # 根据页面名称切换页面
        for i in range(self.stacked_widget.count()):
            if self.stacked_widget.widget(i).objectName() == page_name:
                self.stacked_widget.setCurrentIndex(i)
                break

    def update_animations(self):
        # 更新动态元素
        pass

    # 功能实现方法
    def start_feature_extraction(self):
        """开始特征提取"""
        if not hasattr(self, 'feature_file_path') or not self.feature_file_path:
            QMessageBox.warning(self, "警告", "请先选择要处理的文件！")
            return

        # 检查是否选择了特征提取选项
        selected_options = {
            "文本特征": [name for name, checkbox in self.feature_options["文本特征"].items() if checkbox.isChecked()],
            "数值特征": [name for name, checkbox in self.feature_options["数值特征"].items() if checkbox.isChecked()]
        }

        if not any(selected_options.values()):
            QMessageBox.warning(self, "警告", "请至少选择一个特征提取选项！")
            return

        self.feature_progress.show()
        self.feature_progress.setValue(0)

        try:
            # 首先检查文件大小
            file_size = os.path.getsize(self.feature_file_path) / (1024 * 1024)  # 转换为MB
            if file_size > 500:  # 如果文件大于500MB
                response = QMessageBox.question(
                    self,
                    "文件较大",
                    f"文件大小为 {file_size:.1f}MB，处理可能需要较长时间。是否继续？",
                    QMessageBox.Yes | QMessageBox.No
                )
                if response == QMessageBox.No:
                    self.feature_progress.hide()
                    return

            # 检测文件编码
            encodings = ['utf-8', 'gbk', 'gb2312', 'gb18030', 'big5', 'utf-16', 'iso-8859-1']
            detected_encoding = None

            try:
                import chardet
                with open(self.feature_file_path, 'rb') as file:
                    raw_data = file.read(1024)  # 只读取前1024字节来检测编码
                    result = chardet.detect(raw_data)
                    if result['encoding']:
                        detected_encoding = result['encoding']
                        encodings.insert(0, detected_encoding)
            except ImportError:
                pass

            # 分块读取文件
            chunk_size = 10000  # 每次读取10000行
            chunks = []
            total_chunks = 0

            # 尝试不同的编码读取文件
            success = False
            last_error = None

            for encoding in encodings:
                try:
                    if self.feature_file_path.endswith('.csv'):
                        # 首先计算总行数
                        with open(self.feature_file_path, 'r', encoding=encoding) as f:
                            total_chunks = sum(1 for _ in f) // chunk_size + 1

                        # 然后分块读取
                        chunks = []
                        current_progress = 0
                        progress_step = 80 / total_chunks

                        for chunk in pd.read_csv(self.feature_file_path, encoding=encoding, chunksize=chunk_size):
                            try:
                                # 处理每个数据块
                                chunk = self._process_feature_chunk(chunk, selected_options)
                                chunks.append(chunk)

                                current_progress += progress_step
                                self.feature_progress.setValue(int(current_progress))
                                QApplication.processEvents()
                            except Exception as e:
                                QMessageBox.warning(self, "警告", f"处理数据块时出错：{str(e)}\n继续处理其他数据...")
                                continue

                    success = True
                    break
                except Exception as e:
                    last_error = str(e)
                    continue

            if not success:
                raise Exception(f"无法读取文件，尝试了以下编码：{', '.join(encodings)}\n最后的错误：{last_error}")

            # 合并所有数据块
            try:
                final_df = pd.concat(chunks, ignore_index=True)
            except Exception as e:
                raise Exception(f"合并数据块失败：{str(e)}")

            # 保存处理后的文件
            self.feature_progress.setValue(90)

            # 修改保存文件的路径
            file_name = os.path.splitext(os.path.basename(self.feature_file_path))[0]
            file_ext = os.path.splitext(self.feature_file_path)[1]
            new_file_name = f"{file_name}_features{file_ext}"
            save_path = os.path.join(self.file_manager_dir, new_file_name)
            save_path = self.get_unique_filename(save_path)

            # 分块保存，使用检测到的编码或UTF-8
            if file_ext == '.csv':
                final_df.to_csv(save_path, index=False, encoding=detected_encoding or 'utf-8-sig', chunksize=chunk_size)
            else:
                final_df.to_excel(save_path, index=False)

            self.feature_progress.setValue(100)

            # 显示成功消息
            success_message = (
                "特征提取完成！\n"
                f"处理前行数：{len(final_df)}\n"
                f"使用的文件编码：{detected_encoding or 'utf-8'}\n"
                "已应用以下特征提取方法：\n"
            )

            if selected_options["文本特征"]:
                success_message += "\n文本特征：\n- " + "\n- ".join(selected_options["文本特征"])
            if selected_options["数值特征"]:
                success_message += "\n数值特征：\n- " + "\n- ".join(selected_options["数值特征"])

            success_message += f"\n\n特征提取后的文件已保存为：{new_file_name}"

            QMessageBox.information(self, "成功", success_message)
            self.refresh_file_list()

        except MemoryError:
            QMessageBox.critical(self, "错误", "内存不足，请尝试处理更小的文件或减少选择的特征提取选项。")
        except Exception as e:
            QMessageBox.critical(self, "错误", f"特征提取失败：{str(e)}")
        finally:
            self.feature_progress.hide()

    def _process_feature_chunk(self, chunk, selected_options):
        """处理单个数据块的特征提取"""
        try:
            # 识别列的数据类型
            numeric_cols = chunk.select_dtypes(include=[np.number]).columns
            text_cols = chunk.select_dtypes(include=['object']).columns

            # 数值特征处理
            if selected_options["数值特征"]:
                if "统计特征" in selected_options["数值特征"]:
                    for col in numeric_cols:
                        chunk[f'{col}_mean'] = chunk[col].mean()
                        chunk[f'{col}_std'] = chunk[col].std()
                        chunk[f'{col}_skew'] = chunk[col].skew()
                        chunk[f'{col}_kurt'] = chunk[col].kurtosis()

                if "降维技术" in selected_options["数值特征"]:
                    if len(numeric_cols) > 1:  # 需要至少2个特征才能进行PCA
                        try:
                            from sklearn.decomposition import PCA
                            pca = PCA(n_components=min(3, len(numeric_cols)))
                            pca_result = pca.fit_transform(chunk[numeric_cols])
                            for i in range(pca_result.shape[1]):
                                chunk[f'PCA_{i + 1}'] = pca_result[:, i]
                        except Exception as e:
                            print(f"PCA处理出错：{str(e)}")

                if "数据变换" in selected_options["数值特征"]:
                    for col in numeric_cols:
                        # 添加多项式特征
                        chunk[f'{col}_squared'] = chunk[col] ** 2
                        # 添加对数变换（处理非负值）
                        if chunk[col].min() >= 0:
                            chunk[f'{col}_log'] = np.log1p(chunk[col])

            # 文本特征处理
            if selected_options["文本特征"]:
                for col in text_cols:
                    if "词干提取" in selected_options["文本特征"]:
                        try:
                            from nltk.stem import PorterStemmer
                            ps = PorterStemmer()
                            chunk[f'{col}_stem'] = chunk[col].astype(str).apply(
                                lambda x: ' '.join([ps.stem(word) for word in str(x).split()])
                            )
                        except Exception as e:
                            print(f"词干提取出错：{str(e)}")

                    if "TF-IDF" in selected_options["文本特征"]:
                        try:
                            from sklearn.feature_extraction.text import TfidfVectorizer
                            tfidf = TfidfVectorizer(max_features=5)  # 限制特征数量
                            text_data = chunk[col].astype(str).values
                            tfidf_result = tfidf.fit_transform(text_data)
                            feature_names = tfidf.get_feature_names_out()

                            for i, feature in enumerate(feature_names):
                                chunk[f'{col}_tfidf_{feature}'] = tfidf_result[:, i].toarray()
                        except Exception as e:
                            print(f"TF-IDF处理出错：{str(e)}")

                    if "Word2Vec" in selected_options["文本特征"]:
                        try:
                            from gensim.models import Word2Vec
                            import jieba
                            # 分词
                            sentences = [jieba.lcut(str(text)) for text in chunk[col]]
                            # 训练Word2Vec模型
                            model = Word2Vec(sentences, vector_size=5, window=5, min_count=1, workers=4)

                            # 计算每个文本的平均词向量
                            def get_mean_vector(words):
                                vectors = [model.wv[word] for word in words if word in model.wv]
                                if vectors:
                                    return np.mean(vectors, axis=0)
                                return np.zeros(5)

                            word2vec_features = [get_mean_vector(text) for text in sentences]
                            for i in range(5):
                                chunk[f'{col}_w2v_{i}'] = [vec[i] for vec in word2vec_features]
                        except Exception as e:
                            print(f"Word2Vec处理出错：{str(e)}")

            return chunk
        except Exception as e:
            raise Exception(f"处理特征块时出错：{str(e)}")

    def start_ml_training(self):
        try:
            # 获取选择的数据和参数
            file_path = self.ml_file_path.text()
            if not file_path:
                raise ValueError("请选择数据文件")

            feature_cols = [item.text() for item in self.feature_cols.selectedItems()]
            target_col = self.target_col.currentText()

            if not feature_cols:
                raise ValueError("请选择特征列")
            if not target_col:
                raise ValueError("请选择目标列")

            # 读取数据
            data = pd.read_csv(file_path)
            X = data[feature_cols]
            y = data[target_col]

            # 获取训练参数
            test_size = self.test_size.value()
            random_state = self.random_seed.value()

            # 创建模型
            ml_models = MLModels(test_size=test_size, random_state=random_state)
            # 获取选中的模型
            selected_models = [name for name, cb in self.sup_checks.items() if cb.isChecked()]
            if not selected_models:
                raise ValueError("请至少选择一个训练模型")

            # 清空结果和进度
            self.ml_results.clear()
            total = len(selected_models)
            for idx, model_name in enumerate(selected_models, start=1):
                # 依次调用对应算法
                if model_name == "线性回归":
                    results = ml_models.linear_regression(X, y)
                elif model_name == "逻辑回归":
                    results = ml_models.logistic_regression(X, y)
                else:
                    results = ml_models.hmm_model(X, y)
                # 显示该模型结果
                text = f"模型: {model_name}\n"
                text += f"MSE: {results.get('mse', 'N/A')}\n"
                if 'r2_score' in results:
                    text += f"R²: {results['r2_score']:.4f}\n"
                if 'accuracy' in results:
                    text += f"准确率: {results['accuracy']:.4f}\n"
                    text += f"精确率: {results['precision']:.4f}\n"
                    text += f"召回率: {results['recall']:.4f}\n"
                    text += f"F1: {results['f1']:.4f}\n"
                self.ml_results.append(text + "\n")
                # 更新进度
                self.ml_progress.setValue(int(idx / total * 100))

        except Exception as e:
            QMessageBox.warning(self, "错误", str(e))

    def start_nn_training(self):
        try:
            # 获取选择的数据和参数
            file_path = self.nn_file_path.text()
            if not file_path:
                raise ValueError("请选择数据文件")

            # 特征列和目标列
            feature_cols = [item.text() for item in self.nn_feature_cols.selectedItems()]
            target_col = self.nn_target_col.currentText()
            if not feature_cols:
                raise ValueError("请选择特征列")
            if not target_col:
                raise ValueError("请选择目标列")

            # 读取数据
            data = pd.read_csv(file_path)
            X = data[feature_cols]
            y = data[target_col]

            # 获取训练参数
            test_size = self.nn_test_size.value()
            random_state = self.nn_random_seed.value()
            timesteps = self.timesteps.value()

            # 创建模型
            nn_models = NeuralNetworks(test_size=test_size, random_state=random_state)
            # 获取选中的神经网络模型
            selected_models = [name for name, cb in self.nn_model_checks.items() if cb.isChecked()]
            if not selected_models:
                raise ValueError("请至少选择一个神经网络模型")
            # 清空结果和进度
            self.nn_results.clear()
            total = len(selected_models)
            for idx, model_name in enumerate(selected_models, start=1):
                if model_name == "RNN":
                    results = nn_models.train_rnn(X, y, timesteps=timesteps)
                else:
                    results = nn_models.train_bilstm(X, y, timesteps=timesteps)
                # 显示该模型结果
                text = f"模型: {model_name}\n"
                text += f"MSE: {results['mse']:.4f}\n"
                text += f"准确率: {results['accuracy']:.4f}\n"
                text += f"精确率: {results['precision']:.4f}\n"
                text += f"召回率: {results['recall']:.4f}\n"
                text += f"F1: {results['f1']:.4f}\n"
                self.nn_results.append(text + "\n")
                # 更新进度条
                self.nn_progress.setValue(int(idx / total * 100))
        except Exception as e:
            QMessageBox.warning(self, "错误", str(e))

    def handle_file_operation(self, operation):
        if operation == "上传文件":
            file_path, _ = QFileDialog.getOpenFileName(
                self,
                "选择文件",
                "",
                "数据文件 (*.csv *.xlsx *.txt);;所有文件 (*.*)"
            )
            if file_path:
                # TODO: 处理文件上传
                pass
        elif operation == "查看文件":
            # TODO: 实现文件浏览
            pass
        elif operation == "导出结果":
            # TODO: 实现结果导出
            pass
        elif operation == "文件预览":
            # TODO: 实现文件预览
            pass

    def start_monitoring(self):
        try:
            # 初始化摄像头
            self.cap = cv2.VideoCapture(0)
            if not self.cap.isOpened():
                QMessageBox.warning(self, "错误", "无法打开摄像头")
                return

            # 设置定时器更新视频帧
            self.monitoring_timer = QTimer(self)
            self.monitoring_timer.timeout.connect(self.process_video_frame)
            self.monitoring_timer.start(20)

            # 更新按钮状态
            self.start_btn.setEnabled(False)
            self.stop_btn.setEnabled(True)
            self.status_label.setText("状态: 监测中...")

        except Exception as e:
            QMessageBox.warning(self, "错误", f"启动监测失败: {str(e)}")

    def stop_monitoring(self):
        try:
            # 停止定时器
            if hasattr(self, 'monitoring_timer'):
                self.monitoring_timer.stop()

            # 保存状态
            if hasattr(self, 'face_processor'):
                status = self.face_processor.save_status()
                print(f"状态保存结果: {status}")

            # 释放摄像头
            if hasattr(self, 'cap'):
                self.cap.release()

            # 清空视频显示
            self.video_label.clear()

            # 更新按钮状态
            self.start_btn.setEnabled(True)
            self.stop_btn.setEnabled(False)
            self.status_label.setText("状态: 已停止监测")

        except Exception as e:
            QMessageBox.warning(self, "错误", f"停止监测失败: {str(e)}")

    def process_video_frame(self):
        try:
            ret, frame = self.cap.read()
            if ret:
                self.face_processor.process_frame(frame)
        except Exception as e:
            print(f"处理视频帧时发生错误: {e}")

    def update_video_frame(self, frame):
        try:
            # 将 OpenCV 的 BGR 图像转换为 QImage
            rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            h, w, c = rgb_image.shape
            qt_image = QImage(rgb_image.data, w, h, 3 * w, QImage.Format_RGB888)

            # 显示图片
            self.video_label.setPixmap(QPixmap.fromImage(qt_image))
        except Exception as e:
            print(f"更新视频帧时发生错误: {e}")

    def update_monitoring_status(self, status):
        try:
            # 更新状态显示
            self.blink_count_label.setText(f"眨眼次数: {status['blink_count']}")
            self.eye_rub_count_label.setText(f"揉眼次数: {status['eye_rub_count']}")

            # 根据眨眼和揉眼次数判断疲劳状态
            if status['blink_count'] > 30 or status['eye_rub_count'] > 5:
                self.fatigue_status_label.setText("疲劳状态: 疲劳")
                self.fatigue_status_label.setStyleSheet("color: red; font-size: 14px;")
            else:
                self.fatigue_status_label.setText("疲劳状态: 正常")
                self.fatigue_status_label.setStyleSheet("color: white; font-size: 14px;")

        except Exception as e:
            print(f"更新监测状态时发生错误: {e}")

    def upload_file(self):
        file_dialog = QFileDialog()
        file_path, _ = file_dialog.getOpenFileName(
            self,
            "选择文件",
            "",
            "数据文件 (*.csv *.xlsx *.xls);;所有文件 (*.*)"
        )

        if file_path:
            try:
                # 获取文件名
                file_name = os.path.basename(file_path)
                # 目标路径
                target_path = os.path.join(self.file_manager_dir, file_name)

                # 复制文件到目标目录
                shutil.copy2(file_path, target_path)

                QMessageBox.information(self, "成功", f"文件 {file_name} 上传成功！")
                self.refresh_file_list()
            except Exception as e:
                QMessageBox.critical(self, "错误", f"文件上传失败：{str(e)}")

    def refresh_file_list(self):
        self.file_list_widget.setRowCount(0)

        # 确保文件管理目录存在
        if not os.path.exists(self.file_manager_dir):
            os.makedirs(self.file_manager_dir)

        for file_name in os.listdir(self.file_manager_dir):
            if file_name.endswith(('.csv', '.xlsx', '.xls')):
                row_position = self.file_list_widget.rowCount()
                self.file_list_widget.insertRow(row_position)

                # 文件名
                self.file_list_widget.setItem(row_position, 0, QTableWidgetItem(file_name))

                # 文件大小
                size = os.path.getsize(os.path.join(self.file_manager_dir, file_name))
                size_str = f"{size / 1024:.1f} KB" if size < 1024 * 1024 else f"{size / 1024 / 1024:.1f} MB"
                self.file_list_widget.setItem(row_position, 1, QTableWidgetItem(size_str))

                # 上传时间
                mtime = os.path.getmtime(os.path.join(self.file_manager_dir, file_name))
                date_str = datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M:%S")
                self.file_list_widget.setItem(row_position, 2, QTableWidgetItem(date_str))

                # 操作按钮
                btn_widget = QWidget()
                btn_layout = QHBoxLayout()
                btn_layout.setContentsMargins(5, 2, 5, 2)  # 减小边距
                btn_layout.setSpacing(10)  # 设置按钮间距

                preview_btn = QPushButton("预览")
                preview_btn.setFixedSize(80, 30)  # 固定按钮大小
                preview_btn.setStyleSheet("""
                    QPushButton {
                        background-color: #1a237e;
                        color: white;
                        border-radius: 5px;
                        border: 1px solid #4fc3f7;
                    }
                    QPushButton:hover {
                        background-color: #0d47a1;
                    }
                """)
                preview_btn.clicked.connect(
                    lambda checked, f=os.path.join(self.file_manager_dir, file_name): self.preview_file(f))

                delete_btn = QPushButton("删除")
                delete_btn.setFixedSize(80, 30)  # 固定按钮大小
                delete_btn.setStyleSheet("""
                    QPushButton {
                        background-color: #b71c1c;
                        color: white;
                        border-radius: 5px;
                        border: 1px solid #ff5252;
                    }
                    QPushButton:hover {
                        background-color: #d32f2f;
                    }
                """)
                delete_btn.clicked.connect(
                    lambda checked, f=os.path.join(self.file_manager_dir, file_name): self.delete_file(f))

                btn_layout.addWidget(preview_btn)
                btn_layout.addWidget(delete_btn)
                btn_widget.setLayout(btn_layout)

                self.file_list_widget.setCellWidget(row_position, 3, btn_widget)

        # 设置行高
        for row in range(self.file_list_widget.rowCount()):
            self.file_list_widget.setRowHeight(row, 40)

    def preview_file(self, file_path):
        try:
            # 创建新的预览窗口
            preview_dialog = QDialog(None)
            preview_dialog.setWindowTitle(f"文件预览 - {os.path.basename(file_path)}")
            preview_dialog.resize(1000, 700)
            preview_dialog.setWindowFlags(Qt.Window)

            layout = QVBoxLayout()

            # 创建表格
            table = QTableWidget()
            table.setEditTriggers(QTableWidget.NoEditTriggers)

            # 设置表格样式
            table.setStyleSheet("""
                QTableWidget {
                    background-color: #1e2329;
                    color: white;
                    gridline-color: #4fc3f7;
                    border: none;
                }
                QHeaderView::section {
                    background-color: #0d47a1;
                    color: white;
                    border: 1px solid #4fc3f7;
                    padding: 5px;
                }
                QScrollBar:horizontal, QScrollBar:vertical {
                    border: none;
                    background: #1e2329;
                    height: 14px;
                    width: 14px;
                }
                QScrollBar::handle:horizontal, QScrollBar::handle:vertical {
                    background: #0d47a1;
                    border-radius: 4px;
                }
                QTableWidget::item {
                    padding: 5px;
                }
            """)

            # 设置表格的大小策略
            table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
            table.horizontalHeader().setStretchLastSection(False)
            table.setHorizontalScrollMode(QTableWidget.ScrollPerPixel)
            table.setVerticalScrollMode(QTableWidget.ScrollPerPixel)

            # 添加加载提示标签
            loading_label = QLabel("正在加载数据...")
            loading_label.setStyleSheet("color: #4fc3f7; padding: 5px;")
            loading_label.setAlignment(Qt.AlignCenter)
            layout.addWidget(loading_label)

            # 添加表格到布局
            layout.addWidget(table)

            # 分页控件
            page_control = QWidget()
            page_layout = QHBoxLayout()

            prev_btn = QPushButton("上一页")
            next_btn = QPushButton("下一页")
            page_label = QLabel("第 0/0 页")
            page_size_combo = QComboBox()
            page_size_combo.addItems(["50", "100", "200", "500"])
            goto_page = QLineEdit()
            goto_page.setFixedWidth(60)
            goto_btn = QPushButton("跳转")

            for widget in [prev_btn, next_btn, goto_btn]:
                widget.setFixedSize(80, 30)
                widget.setStyleSheet("""
                    QPushButton {
                        background-color: #1a237e;
                        color: white;
                        border-radius: 5px;
                        border: 1px solid #4fc3f7;
                    }
                    QPushButton:hover {
                        background-color: #0d47a1;
                    }
                    QPushButton:disabled {
                        background-color: #424242;
                        border: 1px solid #757575;
                    }
                """)

            page_layout.addWidget(prev_btn)
            page_layout.addWidget(page_label)
            page_layout.addWidget(next_btn)
            page_layout.addWidget(QLabel("每页行数："))
            page_layout.addWidget(page_size_combo)
            page_layout.addWidget(QLabel("跳转到："))
            page_layout.addWidget(goto_page)
            page_layout.addWidget(goto_btn)

            page_control.setLayout(page_layout)
            layout.addWidget(page_control)

            preview_dialog.setLayout(layout)

            # 创建数据加载线程
            class DataLoader(QThread):
                data_loaded = pyqtSignal(object, str)  # 发送数据和编码信息
                error_occurred = pyqtSignal(str)

                def __init__(self, file_path):
                    super().__init__()
                    self.file_path = file_path
                    self.page = 0
                    self.page_size = 50
                    self.encoding = None
                    self.headers = None
                    self.total_rows = 0

                def run(self):
                    try:
                        # 检测文件编码
                        with open(self.file_path, 'rb') as file:
                            raw_data = file.read()
                            result = chardet.detect(raw_data)
                            detected_encoding = result['encoding']

                        encodings = [detected_encoding] if detected_encoding else []
                        encodings.extend(['utf-8-sig', 'utf-8', 'gbk', 'gb2312', 'iso-8859-1'])

                        # 尝试不同的编码读取文件
                        for enc in encodings:
                            try:
                                if self.file_path.endswith('.csv'):
                                    df = pd.read_csv(self.file_path,
                                                     nrows=self.page_size,
                                                     encoding=enc)
                                    self.headers = df.columns.tolist()
                                    # 计算总行数
                                    with open(self.file_path, 'r', encoding=enc) as f:
                                        self.total_rows = sum(1 for _ in f) - 1
                                    self.encoding = enc
                                    self.data_loaded.emit((df, self.headers, self.total_rows), enc)
                                    break
                                else:
                                    df = pd.read_excel(self.file_path,
                                                       nrows=self.page_size)
                                    self.headers = df.columns.tolist()
                                    self.total_rows = len(pd.read_excel(self.file_path))
                                    self.encoding = 'excel'
                                    self.data_loaded.emit((df, self.headers, self.total_rows), 'excel')
                                    break
                            except Exception:
                                continue

                    except Exception as e:
                        self.error_occurred.emit(str(e))

            # 创建数据加载器实例
            data_loader = DataLoader(file_path)

            # 处理数据加载完成的信号
            def handle_data_loaded(data_tuple, encoding):
                df, headers, total_rows = data_tuple

                # 更新表格
                table.setColumnCount(len(headers))
                table.setHorizontalHeaderLabels(headers)
                table.setRowCount(len(df))

                # 填充数据
                for i, row in enumerate(df.values):
                    for j, value in enumerate(row):
                        item = QTableWidgetItem(str(value))
                        table.setItem(i, j, item)

                # 更新页面信息
                total_pages = (total_rows - 1) // int(page_size_combo.currentText()) + 1
                page_label.setText(f"第 1/{total_pages} 页")
                loading_label.setText(f"数据加载完成 (编码: {encoding})")

                # 启用控件
                prev_btn.setEnabled(False)
                next_btn.setEnabled(total_pages > 1)
                goto_page.setEnabled(True)
                goto_btn.setEnabled(True)

                # 保存状态
                preview_dialog.total_rows = total_rows
                preview_dialog.current_page = 0
                preview_dialog.encoding = encoding

            # 处理错误
            def handle_error(error_msg):
                QMessageBox.critical(preview_dialog, "错误", f"加载数据失败：{error_msg}")
                preview_dialog.close()

            # 连接信号
            data_loader.data_loaded.connect(handle_data_loaded)
            data_loader.error_occurred.connect(handle_error)

            # 异步加载下一页数据
            def load_page_async(page_num):
                try:
                    skip_rows = page_num * int(page_size_combo.currentText()) + 1
                    if preview_dialog.encoding != 'excel':
                        df = pd.read_csv(file_path,
                                         skiprows=range(1, skip_rows),
                                         nrows=int(page_size_combo.currentText()),
                                         encoding=preview_dialog.encoding)
                    else:
                        df = pd.read_excel(file_path,
                                           skiprows=range(1, skip_rows),
                                           nrows=int(page_size_combo.currentText()))

                    table.setRowCount(len(df))
                    for i, row in enumerate(df.values):
                        for j, value in enumerate(row):
                            item = QTableWidgetItem(str(value))
                            table.setItem(i, j, item)

                    preview_dialog.current_page = page_num
                    total_pages = (preview_dialog.total_rows - 1) // int(page_size_combo.currentText()) + 1
                    page_label.setText(f"第 {page_num + 1}/{total_pages} 页")

                    prev_btn.setEnabled(page_num > 0)
                    next_btn.setEnabled((page_num + 1) * int(page_size_combo.currentText()) < preview_dialog.total_rows)

                except Exception as e:
                    QMessageBox.warning(preview_dialog, "警告", f"加载页面失败：{str(e)}")

            # 绑定页面控制事件
            def change_page_size():
                if hasattr(preview_dialog, 'total_rows'):
                    load_page_async(0)

            def goto_page_number():
                try:
                    page_num = int(goto_page.text()) - 1
                    total_pages = (preview_dialog.total_rows - 1) // int(page_size_combo.currentText()) + 1
                    if 0 <= page_num < total_pages:
                        load_page_async(page_num)
                    else:
                        QMessageBox.warning(preview_dialog, "警告", "页码超出范围！")
                except ValueError:
                    QMessageBox.warning(preview_dialog, "警告", "请输入有效的页码！")

            # 连接按钮事件
            prev_btn.clicked.connect(lambda: load_page_async(preview_dialog.current_page - 1))
            next_btn.clicked.connect(lambda: load_page_async(preview_dialog.current_page + 1))
            page_size_combo.currentTextChanged.connect(change_page_size)
            goto_btn.clicked.connect(goto_page_number)
            goto_page.returnPressed.connect(goto_page_number)

            # 开始加载数据
            data_loader.start()

            # 显示对话框
            preview_dialog.show()

        except Exception as e:
            QMessageBox.critical(None, "错误", f"预览文件失败：{str(e)}")

    def remove_preview_dialog(self, dialog):
        """从列表中移除已关闭的预览窗口"""
        if dialog in self.preview_dialogs:
            self.preview_dialogs.remove(dialog)

    def delete_file(self, file_path):
        reply = QMessageBox.question(self, "确认删除",
                                     f"确定要删除文件 {os.path.basename(file_path)} 吗？",
                                     QMessageBox.Yes | QMessageBox.No)

        if reply == QMessageBox.Yes:
            try:
                os.remove(file_path)
                QMessageBox.information(self, "成功", "文件删除成功！")
                self.refresh_file_list()
            except Exception as e:
                QMessageBox.critical(self, "错误", f"文件删除失败：{str(e)}")

    def select_file_for_module(self, module_name):
        """为指定模块选择文件"""
        try:
            # 创建文件选择对话框
            dialog = QDialog(self)
            dialog.setWindowTitle("选择文件")
            dialog.setMinimumWidth(600)
            layout = QVBoxLayout(dialog)

            # 创建文件列表
            file_list = QListWidget()
            file_list.setStyleSheet("""
                QListWidget {
                    background-color: #1e2329;
                    color: white;
                    border: 1px solid #4fc3f7;
                    border-radius: 5px;
                }
                QListWidget::item {
                    padding: 5px;
                }
                QListWidget::item:selected {
                    background-color: #0d47a1;
                }
            """)

            # 获取文件管理目录中的所有文件
            for file_name in os.listdir(self.file_manager_dir):
                if file_name.endswith(('.csv', '.xlsx', '.xls')):
                    file_list.addItem(file_name)

            layout.addWidget(QLabel("请选择要使用的文件："))
            layout.addWidget(file_list)

            # 添加按钮
            button_layout = QHBoxLayout()
            select_btn = QPushButton("选择")
            select_btn.setStyleSheet("""
                QPushButton {
                    background-color: #1a237e;
                    color: white;
                    border-radius: 5px;
                    border: 1px solid #4fc3f7;
                    padding: 5px 20px;
                }
                QPushButton:hover {
                    background-color: #0d47a1;
                }
            """)
            cancel_btn = QPushButton("取消")
            cancel_btn.setStyleSheet(select_btn.styleSheet())

            button_layout.addWidget(select_btn)
            button_layout.addWidget(cancel_btn)
            layout.addLayout(button_layout)

            # 绑定按钮事件
            select_btn.clicked.connect(dialog.accept)
            cancel_btn.clicked.connect(dialog.reject)

            if dialog.exec_() == QDialog.Accepted and file_list.currentItem():
                selected_file = file_list.currentItem().text()
                file_path = os.path.join(self.file_manager_dir, selected_file)

                # 更新对应模块的文件标签和路径
                if module_name == "feature_extraction":
                    self.feature_file_label.setText(selected_file)
                    self.feature_file_path = file_path
                elif module_name == "ml":
                    self.ml_file_label.setText(selected_file)
                    # 设置路径到 QLineEdit，而非覆盖属性
                    self.ml_file_path.setText(file_path)
                    # 文件路径更新后，刷新可选的特征列和目标列
                    self.update_ml_columns()
                elif module_name == "nn":
                    self.nn_file_label.setText(selected_file)
                    # 设置路径到 QLineEdit，而非覆盖属性
                    self.nn_file_path.setText(file_path)
                    # 文件路径更新后，刷新可选的特征列和目标列
                    self.update_nn_columns()
                elif module_name == "visualization":
                    self.viz_file_label.setText(selected_file)
                    self.viz_file_path = file_path
                    # 读取文件并更新列选择下拉框
                    self.update_column_list(file_path)
                elif module_name == "clean":
                    self.clean_file_label.setText(selected_file)
                    self.clean_file_path = file_path
                elif module_name == "model_comparison":
                    self.model_comp_file_label.setText(selected_file)
                    self.model_comp_file_path.setText(file_path)
                    self.start_comparison_btn.setEnabled(True)
                    # 清空之前的结果
                    self.results_text.clear()
                    for progress_bar in self.model_progress_bars.values():
                        progress_bar.setValue(0)
                    # 如果plot_widget有布局，清除之前的图表
                    if self.plot_widget.layout():
                        while self.plot_widget.layout().count():
                            item = self.plot_widget.layout().takeAt(0)
                            widget = item.widget()
                            if widget:
                                widget.deleteLater()

                # 显示成功消息
                QMessageBox.information(self, "成功", f"已选择文件：{selected_file}")

        except Exception as e:
            QMessageBox.critical(self, "错误", f"选择文件时发生错误：{str(e)}")

    def update_column_list(self, file_path):
        """更新可视化模块的列选择下拉框"""
        try:
            # 首先尝试使用 chardet 检测文件编码
            with open(file_path, 'rb') as file:
                raw_data = file.read()
                result = chardet.detect(raw_data)
                detected_encoding = result['encoding']

            # 准备编码列表，将检测到的编码放在最前面
            encodings = [detected_encoding] if detected_encoding else []
            encodings.extend(['utf-8', 'gbk', 'gb2312', 'iso-8859-1', 'utf-16', 'big5'])

            df = None
            last_error = None

            # 尝试读取文件
            if file_path.endswith('.csv'):
                for encoding in encodings:
                    try:
                        # 使用 pandas 的低级读取器，处理编码问题
                        with open(file_path, 'rb') as f:
                            df = pd.read_csv(f, encoding=encoding, engine='python')
                        break
                    except Exception as e:
                        last_error = str(e)
                        continue
            else:
                try:
                    df = pd.read_excel(file_path)
                except Exception as e:
                    last_error = str(e)

            if df is None:
                raise Exception(f"无法读取文件，最后一次错误：{last_error}")

                # 更新机器学习页面的列表
                self.feature_cols.clear()
                self.target_col.clear()
                self.feature_cols.addItems(df.columns.tolist())
                self.target_col.addItems(df.columns.tolist())

                # 更新神经网络页面的列表
                self.nn_feature_cols.clear()
                self.nn_target_col.clear()
                self.nn_feature_cols.addItems(df.columns.tolist())
                self.nn_target_col.addItems(df.columns.tolist())
            # 清空并更新列选择下拉框
            self.column_combo.clear()
            self.column_combo.addItems(df.columns.tolist())

            # 更新文件标签
            self.viz_file_label.setText(os.path.basename(file_path))

            # 存储数据框以供后续使用
            self.current_df = df

            # 显示成功消息
            QMessageBox.information(self, "成功", "文件加载成功！")

        except Exception as e:
            QMessageBox.critical(self, "错误", f"读取文件失败：{str(e)}\n请确保文件格式正确且未被损坏。")
            self.viz_file_label.setText("未选择文件")
            self.column_combo.clear()

    def start_ml_training(self):
        try:
            # 获取选择的数据和参数
            file_path = self.ml_file_path.text()
            if not file_path:
                raise ValueError("请选择数据文件")

            feature_cols = [item.text() for item in self.feature_cols.selectedItems()]
            target_col = self.target_col.currentText()

            if not feature_cols:
                raise ValueError("请选择特征列")
            if not target_col:
                raise ValueError("请选择目标列")

            # 读取数据
            data = pd.read_csv(file_path)
            X = data[feature_cols]
            y = data[target_col]

            # 获取训练参数
            test_size = self.test_size.value()
            random_state = self.random_seed.value()

            # 创建模型
            ml_models = MLModels(test_size=test_size, random_state=random_state)
            # 获取选中的模型
            selected_models = [name for name, cb in self.sup_checks.items() if cb.isChecked()]
            if not selected_models:
                raise ValueError("请至少选择一个训练模型")

            # 清空结果和进度
            self.ml_results.clear()
            total = len(selected_models)
            for idx, model_name in enumerate(selected_models, start=1):
                # 依次调用对应算法
                if model_name == "线性回归":
                    results = ml_models.linear_regression(X, y)
                elif model_name == "逻辑回归":
                    results = ml_models.logistic_regression(X, y)
                else:
                    results = ml_models.hmm_model(X, y)
                # 显示该模型结果
                text = f"模型: {model_name}\n"
                text += f"MSE: {results.get('mse', 'N/A')}\n"
                if 'r2_score' in results:
                    text += f"R²: {results['r2_score']:.4f}\n"
                if 'accuracy' in results:
                    text += f"准确率: {results['accuracy']:.4f}\n"
                    text += f"精确率: {results['precision']:.4f}\n"
                    text += f"召回率: {results['recall']:.4f}\n"
                    text += f"F1: {results['f1']:.4f}\n"
                self.ml_results.append(text + "\n")
                # 更新进度
                self.ml_progress.setValue(int(idx / total * 100))

        except Exception as e:
            QMessageBox.warning(self, "错误", str(e))

    def start_nn_training(self, network_type):
        try:
            # 获取选择的数据和参数
            file_path = self.nn_file_path.text()
            if not file_path:
                raise ValueError("请选择数据文件")

            feature_cols = [item.text() for item in self.nn_feature_cols.selectedItems()]
            target_col = self.nn_target_col.currentText()

            if not feature_cols:
                raise ValueError("请选择特征列")
            if not target_col:
                raise ValueError("请选择目标列")

            # 读取数据
            data = pd.read_csv(file_path)
            X = data[feature_cols]
            y = data[target_col]

            # 获取训练参数
            test_size = self.nn_test_size.value()
            random_state = self.nn_random_seed.value()
            timesteps = self.timesteps.value()

            # 创建模型
            nn_models = NeuralNetworks(test_size=test_size, random_state=random_state)
            model_name = self.nn_model_select.currentText()

            # 设置进度条
            self.nn_progress.setValue(0)

            # 在新线程中训练模型
            class TrainingThread(QThread):
                finished = pyqtSignal(dict)
                progress = pyqtSignal(int)

                def run(self):
                    try:
                        if model_name == "RNN":
                            results = nn_models.train_rnn(X, y, timesteps=timesteps)
                        else:  # BiLSTM
                            results = nn_models.train_bilstm(X, y, timesteps=timesteps)

                        self.finished.emit(results)
                    except Exception as e:
                        self.finished.emit({"error": str(e)})

            def handle_results(results):
                if "error" in results:
                    QMessageBox.warning(self, "错误", f"训练失败: {results['error']}")
                    return

                # 显示结果
                result_text = f"模型: {model_name}\n"
                result_text += f"MSE: {results['mse']}\n"
                result_text += f"准确率: {results['accuracy']}\n"
                result_text += f"精确率: {results['precision']}\n"
                result_text += f"召回率: {results['recall']}\n"
                result_text += f"F1分数: {results['f1']}\n"

                self.nn_results.setText(result_text)
                self.nn_progress.setValue(100)

            self.training_thread = TrainingThread()
            self.training_thread.finished.connect(handle_results)
            self.training_thread.progress.connect(self.nn_progress.setValue)
            self.training_thread.start()

        except Exception as e:
            QMessageBox.warning(self, "错误", str(e))

    def update_ml_columns(self):
        """更新机器学习页面的列选择"""
        try:
            file_path = self.ml_file_path.text()
            if not file_path:
                return

            # 读取数据文件
            if file_path.endswith('.csv'):
                df = pd.read_csv(file_path)
            else:
                df = pd.read_excel(file_path)

            # 更新特征列和目标列的选择
            self.feature_cols.clear()
            self.target_col.clear()
            columns = df.columns.tolist()
            self.feature_cols.addItems(columns)
            self.target_col.addItems(columns)

        except Exception as e:
            QMessageBox.warning(self, "错误", f"更新列选择时发生错误：{str(e)}")

    def update_nn_columns(self):
        """更新神经网络页面的列选择"""
        try:
            file_path = self.nn_file_path.text()
            if not file_path:
                return

            # 读取数据文件
            if file_path.endswith('.csv'):
                df = pd.read_csv(file_path)
            else:
                df = pd.read_excel(file_path)

            # 更新特征列和目标列的选择
            self.nn_feature_cols.clear()
            self.nn_target_col.clear()
            columns = df.columns.tolist()
            self.nn_feature_cols.addItems(columns)
            self.nn_target_col.addItems(columns)

        except Exception as e:
            QMessageBox.warning(self, "错误", f"更新列选择时发生错误：{str(e)}")

    def create_model_comparison_page(self):
        """创建模型比较页面"""
        page = QWidget()
        layout = QVBoxLayout()

        # 文件选择部分
        file_group = QGroupBox("数据文件选择")
        file_layout = QHBoxLayout()
        self.comparison_data_path_label = QLineEdit()
        self.comparison_data_path_label.setReadOnly(True)
        select_file_btn = QPushButton("选择文件")
        select_file_btn.clicked.connect(self.select_comparison_data)
        file_layout.addWidget(self.comparison_data_path_label)
        file_layout.addWidget(select_file_btn)
        file_group.setLayout(file_layout)

        # 开始比较按钮
        self.start_comparison_btn = QPushButton("开始比较")
        self.start_comparison_btn.clicked.connect(self.start_model_comparison)
        self.start_comparison_btn.setEnabled(False)

        # 创建选项卡组件
        self.results_tabs = QTabWidget()

        # 训练过程选项卡
        training_tab = QWidget()
        training_layout = QVBoxLayout()
        self.training_progress = QTextEdit()
        self.training_progress.setReadOnly(True)
        training_layout.addWidget(self.training_progress)
        training_tab.setLayout(training_layout)

        # 损失函数选项卡
        loss_tab = QWidget()
        loss_layout = QVBoxLayout()
        self.loss_figure = plt.figure()
        self.loss_canvas = FigureCanvas(self.loss_figure)
        loss_layout.addWidget(self.loss_canvas)
        loss_tab.setLayout(loss_layout)

        # 结果显示选项卡
        results_tab = QWidget()
        results_layout = QVBoxLayout()

        # 结果表格
        self.results_table = QTableWidget()
        self.results_table.setColumnCount(6)
        self.results_table.setHorizontalHeaderLabels(['模型', 'MSE', '准确率', '召回率', '精确率', 'F1分数'])
        results_layout.addWidget(self.results_table)

        # 性能指标图表
        self.metrics_figure = plt.figure()
        self.metrics_canvas = FigureCanvas(self.metrics_figure)
        results_layout.addWidget(self.metrics_canvas)
        results_tab.setLayout(results_layout)

        # 添加选项卡
        self.results_tabs.addTab(training_tab, "训练过程")
        self.results_tabs.addTab(loss_tab, "损失函数")
        self.results_tabs.addTab(results_tab, "评估结果")

        # 添加所有组件到主布局
        layout.addWidget(file_group)
        layout.addWidget(self.start_comparison_btn)
        layout.addWidget(self.results_tabs)

        # 添加混淆矩阵可视化选项卡
        self.cm_figure = plt.figure()
        self.cm_canvas = FigureCanvas(self.cm_figure)
        cm_tab = QWidget()
        cm_layout = QVBoxLayout()
        cm_layout.addWidget(self.cm_canvas)
        cm_tab.setLayout(cm_layout)
        self.results_tabs.addTab(cm_tab, "混淆矩阵")

        page.setLayout(layout)
        return page

    def update_model_progress(self, progress_info):
        """更新模型训练进度"""
        model_name = progress_info.get('model', '')
        epoch = progress_info.get('epoch', 0)
        loss = progress_info.get('loss', 0)
        accuracy = progress_info.get('accuracy', 0)
        history = progress_info.get('history', {})

        # 更新训练进度文本
        progress_text = f"{model_name} - Epoch {epoch}: loss = {loss:.4f}, accuracy = {accuracy:.4f}\n"
        self.training_progress.append(progress_text)

        # 更新损失函数图
        if not hasattr(self, 'model_histories'):
            self.model_histories = {}

        if model_name not in self.model_histories:
            self.model_histories[model_name] = history
        else:
            self.model_histories[model_name] = history

        self.loss_figure.clear()
        ax = self.loss_figure.add_subplot(111)

        # 为每个模型使用不同的颜色
        colors = ['b', 'g', 'r', 'c', 'm']
        for i, (model, hist) in enumerate(self.model_histories.items()):
            if hist.get('loss'):
                epochs = range(1, len(hist['loss']) + 1)
                ax.plot(epochs, hist['loss'],
                        label=f'{model} Loss',
                        color=colors[i % len(colors)])
                if hist.get('accuracy'):
                    ax_right = ax.twinx()
                    ax_right.plot(epochs, hist['accuracy'],
                                  label=f'{model} Accuracy',
                                  color=colors[i % len(colors)],
                                  linestyle='--')
                    ax_right.set_ylabel('Accuracy')

        ax.set_xlabel('Epoch')
        ax.set_ylabel('Loss')
        ax.legend(loc='upper left')
        if any(hist.get('accuracy') for hist in self.model_histories.values()):
            ax_right.legend(loc='upper right')

        self.loss_figure.tight_layout()
        self.loss_canvas.draw()

    def update_results_table(self, result):
        """更新结果表格"""
        row = self.results_table.rowCount()
        self.results_table.insertRow(row)

        # 添加结果到表格
        self.results_table.setItem(row, 0, QTableWidgetItem(result['model']))
        self.results_table.setItem(row, 1, QTableWidgetItem(f"{result['mse']:.4f}"))

        metrics = ['accuracy', 'recall', 'precision', 'f1']
        for i, metric in enumerate(metrics, 2):
            value = result.get(metric)
            if value is not None:
                self.results_table.setItem(row, i, QTableWidgetItem(f"{value:.4f}"))
            else:
                self.results_table.setItem(row, i, QTableWidgetItem('N/A'))

        # 调整列宽
        self.results_table.resizeColumnsToContents()

        # 更新性能指标图表
        self.update_metrics_plot()

        # 动态更新混淆矩阵
        if not hasattr(self, 'partial_results'):
            self.partial_results = []
        self.partial_results.append(result)
        try:
            self.cm_figure.clear()
            num = len(self.partial_results)
            cols = 2
            rows = math.ceil(num / cols)
            for idx, r in enumerate(self.partial_results):
                cm = r.get('confusion_matrix')
                if cm is not None:
                    ax = self.cm_figure.add_subplot(rows, cols, idx + 1)
                    sns.heatmap(cm, annot=True, fmt='d', ax=ax)
                    ax.set_title(r['model'])
            self.cm_figure.tight_layout()
            self.cm_canvas.draw()
        except Exception as e:
            print(f"更新动态混淆矩阵失败: {e}")

    def update_metrics_plot(self):
        """更新性能指标图表"""
        self.metrics_figure.clear()
        gs = self.metrics_figure.add_gridspec(3, 2)

        metrics = {
            'MSE': ('mse', gs[0, 0]),
            'Accuracy': ('accuracy', gs[0, 1]),
            'Recall': ('recall', gs[1, 0]),
            'Precision': ('precision', gs[1, 1]),
            'F1 Score': ('f1', gs[2, 0])
        }

        models = []
        metric_values = {metric: [] for metric in metrics.keys()}

        for row in range(self.results_table.rowCount()):
            models.append(self.results_table.item(row, 0).text())
            for col, (metric, _) in enumerate(metrics.values()):
                item = self.results_table.item(row, col + 1)
                if item and item.text() != 'N/A':
                    metric_values[list(metrics.keys())[col]].append(float(item.text()))
                else:
                    metric_values[list(metrics.keys())[col]].append(0)

        for title, (metric, subplot) in metrics.items():
            ax = self.metrics_figure.add_subplot(subplot)
            values = metric_values[title]
            if values:
                ax.bar(models, values)
                ax.set_title(title)
                plt.setp(ax.xaxis.get_majorticklabels(), rotation=45)

        self.metrics_figure.tight_layout()
        self.metrics_canvas.draw()

    def show_final_comparison(self, final_results):
        """显示最终的模型比较结果"""
        # 清空现有结果
        self.results_table.clearContents()
        self.results_table.setRowCount(0)

        if isinstance(final_results, dict) and 'results' in final_results:
            results = final_results['results']
            if isinstance(results, list):
                for result in results:
                    self.update_results_table(result)

        # 绘制混淆矩阵
        try:
            self.cm_figure.clear()
            num = len(results)
            cols = 2
            rows = math.ceil(num / cols)
            for idx, r in enumerate(results):
                cm = r.get('confusion_matrix')
                if cm is not None:
                    ax = self.cm_figure.add_subplot(rows, cols, idx + 1)
                    sns.heatmap(cm, annot=True, fmt='d', ax=ax)
                    ax.set_title(r['model'])
            self.cm_figure.tight_layout()
            self.cm_canvas.draw()
        except Exception as e:
            print(f"绘制混淆矩阵失败: {e}")

        # 启用开始按钮
        self.start_comparison_btn.setEnabled(True)
        self.start_comparison_btn.setText("开始比较")

    def ensure_async_environment(self):
        """确保异步环境正确设置"""
        print("检查异步环境设置...")
        try:
            import asyncio
            import qasync

            # 检查是否有活动的事件循环
            try:
                loop = asyncio.get_event_loop()
                print(f"当前事件循环: {loop}")
            except RuntimeError:
                print("没有活动的事件循环，创建新的循环")
                loop = asyncio.new_event_loop()
                asyncio.set_event_loop(loop)

            # 检查是否在 QAsync 环境中
            app = qasync.QApplication.instance()
            if app is None:
                print("QAsync 应用不存在，创建新的应用")
                app = qasync.QApplication([])

            print("异步环境检查完成")
            return True

        except Exception as e:
            print(f"设置异步环境时出错: {str(e)}")
            import traceback
            traceback.print_exc()
            return False

    def start_model_comparison(self):
        """开始模型比较分析"""
        try:
            # 检查是否选择了数据文件
            if not self.comparison_data_path or not os.path.exists(self.comparison_data_path):
                QMessageBox.warning(self, "警告", "请先选择数据文件！")
                return

            # 禁用开始按钮
            self.start_comparison_btn.setEnabled(False)
            self.start_comparison_btn.setText("正在比较...")

            try:
                # 读取数据文件
                data = pd.read_csv(self.comparison_data_path)

                # 检查必需的列
                required_columns = ['SPEED', 'FINT_LANEVOLUME', 'FINT_SECTVOLUME', 'FSTR_ROADNUMBER', 'Severity']
                missing_columns = [col for col in required_columns if col not in data.columns]
                if missing_columns:
                    QMessageBox.critical(self, "错误", f"数据文件缺少必需的列：{', '.join(missing_columns)}")
                    return

                # 重置进度条和结果显示
                self.progress_bars = {}
                self.loss_curves = {}
                self.results_table.clearContents()
                self.results_table.setRowCount(0)

                # 创建并设置模型比较线程
                self.comparison_thread = ModelComparisonThread(self.model_comparison, data)
                self.comparison_thread.finished.connect(self.on_comparison_finished)
                self.comparison_thread.error.connect(self.on_comparison_error)

                # 启动线程
                self.comparison_thread.start()

            except Exception as e:
                QMessageBox.critical(self, "错误", f"处理数据文件时出错：{str(e)}")
                self.start_comparison_btn.setEnabled(True)
                self.start_comparison_btn.setText("开始比较")
                raise e

        except Exception as e:
            print(f"启动模型比较时出错: {str(e)}")
            import traceback
            traceback.print_exc()
            self.start_comparison_btn.setEnabled(True)
            self.start_comparison_btn.setText("开始比较")

    def on_comparison_finished(self, results):
        """模型比较完成的回调函数"""
        try:
            self.show_final_comparison({'results': results})
            self.start_comparison_btn.setEnabled(True)
            self.start_comparison_btn.setText("开始比较")
            QMessageBox.information(self, "完成", "模型比较分析已完成！")
        except Exception as e:
            print(f"处理比较结果时出错: {str(e)}")
            import traceback
            traceback.print_exc()

    def on_comparison_error(self, error_msg):
        """模型比较出错的回调函数"""
        QMessageBox.critical(self, "错误", f"模型比较过程中出错：{error_msg}")
        self.start_comparison_btn.setEnabled(True)
        self.start_comparison_btn.setText("开始比较")

    def select_comparison_data(self):
        """从文件管理目录中选择数据文件进行模型比较"""
        # 获取文件管理目录中的文件列表
        file_list = []
        for file in os.listdir(self.file_manager_dir):
            if file.endswith('.csv'):
                file_list.append(file)

        if not file_list:
            QMessageBox.warning(self, "警告", "文件管理目录中没有可用的CSV文件，请先上传数据文件。")
            return

        # 创建文件选择对话框
        dialog = QDialog(self)
        dialog.setWindowTitle("选择数据文件")
        dialog.setModal(True)
        layout = QVBoxLayout()

        # 创建文件列表
        list_widget = QListWidget()
        list_widget.addItems(file_list)
        layout.addWidget(list_widget)

        # 添加按钮
        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        button_box.accepted.connect(dialog.accept)
        button_box.rejected.connect(dialog.reject)
        layout.addWidget(button_box)

        dialog.setLayout(layout)

        # 显示对话框
        if dialog.exec_() == QDialog.Accepted and list_widget.currentItem():
            selected_file = list_widget.currentItem().text()
            self.comparison_data_path = os.path.join(self.file_manager_dir, selected_file)
            # 显示文件名而不是完整路径
            self.comparison_data_path_label.setText(selected_file)
            # 启用开始比较按钮
            self.start_comparison_btn.setEnabled(True)


class ModelComparisonThread(QThread):
    finished = pyqtSignal(object)
    error = pyqtSignal(str)

    def __init__(self, model_comparison, data):
        super().__init__()
        self.model_comparison = model_comparison
        self.data = data

    def run(self):
        try:
            # 创建事件循环
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)

            # 运行模型比较
            results = loop.run_until_complete(self.model_comparison.compare_models(self.data))

            # 关闭事件循环
            loop.close()

            # 发送完成信号
            self.finished.emit(results)
        except Exception as e:
            print(f"模型比较线程出错: {str(e)}")
            import traceback
            traceback.print_exc()
            self.error.emit(str(e))


if __name__ == '__main__':
    try:
        import sys
        import asyncio
        import qasync
        import qdarkstyle
        from PyQt5.QtWidgets import QApplication


        def main():
            app = QApplication.instance()
            if app is None:
                app = QApplication(sys.argv)

            app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())

            loop = qasync.QEventLoop(app)
            asyncio.set_event_loop(loop)

            window = MainWindow()
            window.show()

            with loop:
                loop.run_forever()


        main()

    except Exception as e:
        print(f"程序启动时出错: {str(e)}")
        import traceback

        traceback.print_exc()
        sys.exit(1)
